xref: /third_party/node/deps/uv/src/unix/darwin.c (revision 1cb0ef41)
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
38static uv_once_t once = UV_ONCE_INIT;
39static uint64_t (*time_func)(void);
40static mach_timebase_info_data_t timebase;
41
42typedef unsigned char UInt8;
43
44int 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
54void uv__platform_loop_delete(uv_loop_t* loop) {
55  uv__fsevents_loop_delete(loop);
56}
57
58
59static 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
69uint64_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
75int 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
107uint64_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
120uint64_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
132uint64_t uv_get_constrained_memory(void) {
133  return 0;  /* Memory constraints are unknown. */
134}
135
136
137void 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
150int 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
171int 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
186static 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
315out:
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
327int 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