/*!A cross-platform build utility based on Lua * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright (C) 2015-present, Xmake Open Source Community. * * @author ruki * @file cpuinfo.c * */ /* ////////////////////////////////////////////////////////////////////////////////////// * trace */ #define TB_TRACE_MODULE_NAME "cpuinfo" #define TB_TRACE_MODULE_DEBUG (0) /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ #include "prefix.h" #if defined(TB_CONFIG_OS_MACOSX) #include #include #include #include #include #elif defined(TB_CONFIG_OS_WINDOWS) #include #elif defined(TB_CONFIG_OS_LINUX) #include #elif defined(TB_CONFIG_OS_BSD) #include #include #include #include #include #endif /* ////////////////////////////////////////////////////////////////////////////////////// * private implementation */ #if defined(TB_CONFIG_OS_WINDOWS) static tb_uint64_t xm_os_cpuinfo_subtract_times(FILETIME const *one, FILETIME const *two) { LARGE_INTEGER a, b; a.LowPart = one->dwLowDateTime; a.HighPart = one->dwHighDateTime; b.LowPart = two->dwLowDateTime; b.HighPart = two->dwHighDateTime; return (tb_uint64_t)(a.QuadPart - b.QuadPart); } #endif static tb_float_t xm_os_cpuinfo_usagerate() { #if defined(TB_CONFIG_OS_MACOSX) tb_float_t usagerate = 0; natural_t cpu_count = 0; processor_info_array_t cpuinfo; mach_msg_type_number_t cpuinfo_count; static tb_hong_t s_time = 0; if (tb_mclock() - s_time > 1000 && host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, &cpuinfo, &cpuinfo_count) == KERN_SUCCESS) { static processor_info_array_t s_cpuinfo_prev = tb_null; static mach_msg_type_number_t s_cpuinfo_count_prev = 0; for (tb_int_t i = 0; i < cpu_count; ++i) { tb_int_t use, total; if (s_cpuinfo_prev) { use = (cpuinfo[(CPU_STATE_MAX * i) + CPU_STATE_USER] - s_cpuinfo_prev[(CPU_STATE_MAX * i) + CPU_STATE_USER]) + (cpuinfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] - s_cpuinfo_prev[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM]) + (cpuinfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE] - s_cpuinfo_prev[(CPU_STATE_MAX * i) + CPU_STATE_NICE]); total = use + (cpuinfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE] - s_cpuinfo_prev[(CPU_STATE_MAX * i) + CPU_STATE_IDLE]); } else { use = cpuinfo[(CPU_STATE_MAX * i) + CPU_STATE_USER] + cpuinfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] + cpuinfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE]; total = use + cpuinfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE]; } usagerate += total > 0 ? ((tb_float_t)use / (tb_float_t)total) : 0; } if (s_cpuinfo_prev) { vm_deallocate(mach_task_self(), (vm_address_t)s_cpuinfo_prev, sizeof(integer_t) * s_cpuinfo_count_prev); } s_time = tb_mclock(); s_cpuinfo_prev = cpuinfo; s_cpuinfo_count_prev = cpuinfo_count; } return cpu_count > 0 ? usagerate / cpu_count : 0; #elif defined(TB_CONFIG_OS_WINDOWS) // kernel include idle_time tb_float_t usagerate = 0; FILETIME idle, kernel, user; if (GetSystemTimes(&idle, &kernel, &user)) { static FILETIME idle_prev = { 0 }; static FILETIME kernel_prev = { 0 }; static FILETIME user_prev = { 0 }; if (idle_prev.dwLowDateTime != 0 && idle_prev.dwHighDateTime != 0) { tb_uint64_t idle_diff = xm_os_cpuinfo_subtract_times(&idle, &idle_prev); tb_uint64_t kernel_diff = xm_os_cpuinfo_subtract_times(&kernel, &kernel_prev); tb_uint64_t user_diff = xm_os_cpuinfo_subtract_times(&user, &user_prev); // kernel_time - idle_time = kernel_time, because kernel include idle_time tb_uint64_t sys_total = kernel_diff + user_diff; tb_uint64_t kernel_total = kernel_diff - idle_diff; // sometimes kernel_time > idle_time if (sys_total > 0) { usagerate = (tb_float_t)((tb_double_t)(kernel_total + user_diff) / sys_total); } } idle_prev = idle; kernel_prev = kernel; user_prev = user; } return usagerate; #elif defined(TB_CONFIG_OS_LINUX) tb_float_t usagerate = 0; if (tb_file_info("/proc/stat", tb_null)) { FILE *fp = fopen("/proc/stat", "r"); if (fp) { tb_char_t line[8192]; static tb_int64_t total_prev = 0; static tb_int64_t active_prev = 0; while (!feof(fp)) { /* cpu 548760 0 867417 102226682 12430 0 9089 0 0 0 * cpu0 136863 0 218110 25632388 2706 0 2328 0 0 0 * cpu1 148383 0 213941 25627686 3925 0 2129 0 0 0 * * The meanings of the columns are as follows, from left to right: * * user: normal processes executing in user mode * nice: niced processes executing in user mode * system: processes executing in kernel mode * idle: twiddling thumbs * iowait: waiting for I/O to complete * irq: servicing interrupts * softirq: servicing softirqs * steal * guest * guest_nice */ if (fgets(line, sizeof(line), fp) && !tb_strncmp(line, "cpu ", 4)) { long long user, nice, sys, idle, iowait, irq, softirq, steal, guest, guest_nice; if (10 == sscanf(line, "cpu %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld", &user, &nice, &sys, &idle, &iowait, &irq, &softirq, &steal, &guest, &guest_nice)) { tb_int64_t active = (tb_int64_t)(user + nice + sys + irq + softirq + steal + guest + guest_nice); tb_int64_t total = (tb_int64_t)(user + nice + sys + idle + iowait + irq + softirq + steal + guest + guest_nice); if (total_prev > 0 && active_prev > 0) { tb_int64_t total_diff = total - total_prev; tb_int64_t active_diff = active - active_prev; if (total_diff > 0) { usagerate = (tb_float_t)((tb_double_t)active_diff / total_diff); } } total_prev = total; active_prev = active; } break; } } fclose(fp); } } return usagerate; #elif defined(TB_CONFIG_OS_BSD) && !defined(__OpenBSD__) #define CP_USER 0 #define CP_NICE 1 #define CP_SYS 2 #define CP_INTR 3 #define CP_IDLE 4 #define CPUSTATES 5 static tb_int64_t total_prev = 0; static tb_int64_t active_prev = 0; tb_float_t usagerate = 0; long states[CPUSTATES] = { 0 }; size_t states_size = sizeof(states); if (sysctlbyname("kern.cp_time", &states, &states_size, tb_null, 0) == 0) { tb_long_t user = states[CP_USER]; tb_long_t nice = states[CP_NICE]; tb_long_t sys = states[CP_SYS]; tb_long_t intr = states[CP_INTR]; tb_long_t idle = states[CP_IDLE]; tb_int64_t active = user + nice + sys + intr; tb_int64_t total = user + nice + sys + idle + intr; if (total_prev > 0 && active_prev > 0) { tb_int64_t total_diff = total - total_prev; tb_int64_t active_diff = active - active_prev; if (total_diff > 0) { usagerate = (tb_float_t)((tb_double_t)active_diff / total_diff); } } total_prev = total; active_prev = active; } return usagerate; #else return 0; #endif } /* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ /* local cpuinfo = os.cpuinfo() * { * ncpu = 4, * ... * } */ tb_int_t xm_os_cpuinfo(lua_State *lua) { tb_assert_and_check_return_val(lua, 0); // init table lua_newtable(lua); // get cpu number tb_int_t ncpu = (tb_int_t)tb_cpu_count(); lua_pushstring(lua, "ncpu"); lua_pushinteger(lua, ncpu > 0 ? ncpu : 1); lua_settable(lua, -3); // get cpu usage rate tb_float_t usagerate = xm_os_cpuinfo_usagerate(); if (usagerate >= 0) { lua_pushstring(lua, "usagerate"); lua_pushnumber(lua, usagerate); lua_settable(lua, -3); } return 1; }