/* * Copyright (c) 2013-2014 Ruwen Hahn * Hugh "Jim" Bailey * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "base.h" #include "platform.h" #include "dstr.h" #include #include #include #include #include #include #include #include #include #import /* clock function selection taken from libc++ */ static uint64_t ns_time_simple() { return mach_absolute_time(); } static double ns_time_compute_factor() { mach_timebase_info_data_t info = {1, 1}; mach_timebase_info(&info); return ((double)info.numer) / info.denom; } static uint64_t ns_time_full() { static double factor = 0.; if (factor == 0.) factor = ns_time_compute_factor(); return (uint64_t)(mach_absolute_time() * factor); } typedef uint64_t (*time_func)(); static time_func ns_time_select_func() { mach_timebase_info_data_t info = {1, 1}; mach_timebase_info(&info); if (info.denom == info.numer) return ns_time_simple; return ns_time_full; } uint64_t os_gettime_ns(void) { static time_func f = NULL; if (!f) f = ns_time_select_func(); return f(); } /* gets the location [domain mask]/Library/Application Support/[name] */ static int os_get_path_internal(char *dst, size_t size, const char *name, NSSearchPathDomainMask domainMask) { NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, domainMask, YES); if([paths count] == 0) bcrash("Could not get home directory (platform-cocoa)"); NSString *application_support = paths[0]; const char *base_path = [application_support UTF8String]; if (!name || !*name) return snprintf(dst, size, "%s", base_path); else return snprintf(dst, size, "%s/%s", base_path, name); } static char *os_get_path_ptr_internal(const char *name, NSSearchPathDomainMask domainMask) { NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, domainMask, YES); if([paths count] == 0) bcrash("Could not get home directory (platform-cocoa)"); NSString *application_support = paths[0]; NSUInteger len = [application_support lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; char *path_ptr = bmalloc(len+1); path_ptr[len] = 0; memcpy(path_ptr, [application_support UTF8String], len); struct dstr path; dstr_init_move_array(&path, path_ptr); dstr_cat(&path, "/"); dstr_cat(&path, name); return path.array; } int os_get_config_path(char *dst, size_t size, const char *name) { return os_get_path_internal(dst, size, name, NSUserDomainMask); } char *os_get_config_path_ptr(const char *name) { return os_get_path_ptr_internal(name, NSUserDomainMask); } int os_get_program_data_path(char *dst, size_t size, const char *name) { return os_get_path_internal(dst, size, name, NSLocalDomainMask); } char *os_get_program_data_path_ptr(const char *name) { return os_get_path_ptr_internal(name, NSLocalDomainMask); } struct os_cpu_usage_info { int64_t last_cpu_time; int64_t last_sys_time; int core_count; }; static inline void add_time_value(time_value_t *dst, time_value_t *a, time_value_t *b) { dst->microseconds = a->microseconds + b->microseconds; dst->seconds = a->seconds + b->seconds; if (dst->microseconds >= 1000000) { dst->seconds += dst->microseconds / 1000000; dst->microseconds %= 1000000; } } static bool get_time_info(int64_t *cpu_time, int64_t *sys_time) { mach_port_t task = mach_task_self(); struct task_thread_times_info thread_data; struct task_basic_info_64 task_data; mach_msg_type_number_t count; kern_return_t kern_ret; time_value_t cur_time; *cpu_time = 0; *sys_time = 0; count = TASK_THREAD_TIMES_INFO_COUNT; kern_ret = task_info(task, TASK_THREAD_TIMES_INFO, (task_info_t)&thread_data, &count); if (kern_ret != KERN_SUCCESS) return false; count = TASK_BASIC_INFO_64_COUNT; kern_ret = task_info(task, TASK_BASIC_INFO_64, (task_info_t)&task_data, &count); if (kern_ret != KERN_SUCCESS) return false; add_time_value(&cur_time, &thread_data.user_time, &thread_data.system_time); add_time_value(&cur_time, &cur_time, &task_data.user_time); add_time_value(&cur_time, &cur_time, &task_data.system_time); *cpu_time = os_gettime_ns() / 1000; *sys_time = cur_time.seconds * 1000000 + cur_time.microseconds; return true; } os_cpu_usage_info_t *os_cpu_usage_info_start(void) { struct os_cpu_usage_info *info = bmalloc(sizeof(*info)); if (!get_time_info(&info->last_cpu_time, &info->last_sys_time)) { bfree(info); return NULL; } info->core_count = sysconf(_SC_NPROCESSORS_ONLN); return info; } double os_cpu_usage_info_query(os_cpu_usage_info_t *info) { int64_t sys_time, cpu_time; int64_t sys_time_delta, cpu_time_delta; if (!info || !get_time_info(&cpu_time, &sys_time)) return 0.0; sys_time_delta = sys_time - info->last_sys_time; cpu_time_delta = cpu_time - info->last_cpu_time; if (cpu_time_delta == 0) return 0.0; info->last_sys_time = sys_time; info->last_cpu_time = cpu_time; return (double)sys_time_delta * 100.0 / (double)cpu_time_delta / (double)info->core_count; } void os_cpu_usage_info_destroy(os_cpu_usage_info_t *info) { if (info) bfree(info); } os_performance_token_t *os_request_high_performance(const char *reason) { @autoreleasepool { NSProcessInfo *pi = [NSProcessInfo processInfo]; SEL sel = @selector(beginActivityWithOptions:reason:); if (![pi respondsToSelector:sel]) return nil; //taken from http://stackoverflow.com/a/20100906 id activity = [pi beginActivityWithOptions:0x00FFFFFF reason:@(reason)]; return CFBridgingRetain(activity); } } void os_end_high_performance(os_performance_token_t *token) { @autoreleasepool { NSProcessInfo *pi = [NSProcessInfo processInfo]; SEL sel = @selector(beginActivityWithOptions:reason:); if (![pi respondsToSelector:sel]) return; [pi endActivity:CFBridgingRelease(token)]; } } struct os_inhibit_info { CFStringRef reason; IOPMAssertionID sleep_id; IOPMAssertionID user_id; bool active; }; os_inhibit_t *os_inhibit_sleep_create(const char *reason) { struct os_inhibit_info *info = bzalloc(sizeof(*info)); if (!reason) info->reason = CFStringCreateWithCString(kCFAllocatorDefault, reason, kCFStringEncodingUTF8); else info->reason = CFStringCreateCopy(kCFAllocatorDefault, CFSTR("")); return info; } bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active) { IOReturn success; if (!info) return false; if (info->active == active) return false; if (active) { IOPMAssertionDeclareUserActivity(info->reason, kIOPMUserActiveLocal, &info->user_id); success = IOPMAssertionCreateWithName( kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, info->reason, &info->sleep_id); if (success != kIOReturnSuccess) { blog(LOG_WARNING, "Failed to disable sleep"); return false; } } else { IOPMAssertionRelease(info->sleep_id); } info->active = active; return true; } void os_inhibit_sleep_destroy(os_inhibit_t *info) { if (info) { os_inhibit_sleep_set_active(info, false); CFRelease(info->reason); bfree(info); } } static int physical_cores = 0; static int logical_cores = 0; static bool core_count_initialized = false; static void os_get_cores_internal(void) { if (core_count_initialized) return; core_count_initialized = true; size_t size; int ret; size = sizeof(physical_cores); ret = sysctlbyname("machdep.cpu.core_count", &physical_cores, &size, NULL, 0); if (ret != 0) return; ret = sysctlbyname("machdep.cpu.thread_count", &logical_cores, &size, NULL, 0); } int os_get_physical_cores(void) { if (!core_count_initialized) os_get_cores_internal(); return physical_cores; } int os_get_logical_cores(void) { if (!core_count_initialized) os_get_cores_internal(); return logical_cores; }