diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index c8652b05d..10e6b1f81 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -323,6 +323,7 @@ DLL_OFILES:= \ kernel32.o \ ldap.o \ libstdcxx_wrapper.o \ + loadavg.o \ localtime.o \ lsearch.o \ malloc_wrapper.o \ diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index df06013b8..02d2e67d7 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -730,4 +730,9 @@ LoadDLLfunc (WSASetLastError, 4, ws2_32) LoadDLLfunc (WSASocketW, 24, ws2_32) // LoadDLLfunc (WSAStartup, 8, ws2_32) LoadDLLfunc (WSAWaitForMultipleEvents, 20, ws2_32) + +LoadDLLfunc (PdhAddEnglishCounterA, 16, pdh) +LoadDLLfunc (PdhCollectQueryData, 4, pdh) +LoadDLLfunc (PdhGetFormattedCounterValue, 16, pdh) +LoadDLLfunc (PdhOpenQueryA, 12, pdh) } diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index f23681308..8e54a7ddd 100644 --- a/winsup/cygwin/common.din +++ b/winsup/cygwin/common.din @@ -624,6 +624,7 @@ gethostname = cygwin_gethostname SIGFE getifaddrs SIGFE getitimer SIGFE getline = __getline SIGFE +getloadavg SIGFE getlogin NOSIGFE getlogin_r NOSIGFE getmntent SIGFE diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc index 2a8cf1427..a7e816fe8 100644 --- a/winsup/cygwin/fhandler_proc.cc +++ b/winsup/cygwin/fhandler_proc.cc @@ -418,7 +418,7 @@ static off_t format_proc_loadavg (void *, char *&destbuf) { extern int get_process_state (DWORD dwProcessId); - unsigned running = 0; + unsigned int running = 0; winpids pids ((DWORD) 0); for (unsigned i = 0; i < pids.npids; i++) @@ -429,9 +429,13 @@ format_proc_loadavg (void *, char *&destbuf) break; } + double loadavg[3] = { 0.0, 0.0, 0.0 }; + getloadavg (loadavg, 3); + destbuf = (char *) crealloc_abort (destbuf, 48); - return __small_sprintf (destbuf, "%u.%02u %u.%02u %u.%02u %u/%u\n", - 0, 0, 0, 0, 0, 0, running, pids.npids); + return sprintf (destbuf, "%.2f %.2f %.2f %u/%u\n", + loadavg[0], loadavg[1], loadavg[2], running, + (unsigned int)pids.npids); } static off_t diff --git a/winsup/cygwin/include/cygwin/stdlib.h b/winsup/cygwin/include/cygwin/stdlib.h index 744a08d95..a8eb4de31 100644 --- a/winsup/cygwin/include/cygwin/stdlib.h +++ b/winsup/cygwin/include/cygwin/stdlib.h @@ -77,6 +77,10 @@ extern _PTR valloc _PARAMS ((size_t)); #undef _mstats_r #define _mstats_r(r, p) mstats (p) +#if __BSD_VISIBLE +int getloadavg(double loadavg[], int nelem); +#endif + #ifdef __cplusplus } #endif diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 6ca307961..298741a4b 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -473,12 +473,13 @@ details. */ 306: Export getentropy, getrandom. 307: Export timingsafe_bcmp, timingsafe_memcmp. 308: Export dladdr. + 309: Export getloadavg. Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 308 +#define CYGWIN_VERSION_API_MINOR 309 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible changes are made to the shared diff --git a/winsup/cygwin/loadavg.cc b/winsup/cygwin/loadavg.cc new file mode 100644 index 000000000..a7e5f61dd --- /dev/null +++ b/winsup/cygwin/loadavg.cc @@ -0,0 +1,192 @@ +/* loadavg.cc: load average support. + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +/* + Emulate load average + + There's a fair amount of approximation done here, so don't try to use this to + actually measure anything, but it should be good enough for programs to + throttle their activity based on load. + + A global load average estimate is maintained in shared memory. Access to that + is guarded by a mutex. This estimate is only updated at most every 5 seconds. + + We attempt to count running and runnable processes, but unlike linux we don't + count processes in uninterruptible sleep (blocked on I/O). + + The number of running processes is estimated as (NumberOfProcessors) * (% + Processor Time). The number of runnable processes is estimated as + ProcessorQueueLength. + + Note that PDH will only return data for '% Processor Time' afer the second + call to PdhCollectQueryData(), as it's computed over an interval, so the first + attempt to estimate load will fail and 0.0 will be returned. + + We also assume that '% Processor Time' averaged over the interval since the + last time getloadavg() was called is a good approximation of the instantaneous + '% Processor Time'. +*/ + +#include "winsup.h" +#include "shared_info.h" +#include "loadavg.h" + +#include +#include +#include + +/* Prototype for PdhAddEnglishCounterA in pdh.h under _WIN32_WINNT >= 0x0600 is + missing WINAPI */ +#undef _WIN32_WINNT +#include +extern "C" +PDH_FUNCTION PdhAddEnglishCounterA(PDH_HQUERY hQuery, LPCSTR szFullCounterPath, + DWORD_PTR dwUserData, PDH_HCOUNTER *phCounter); + +static PDH_HQUERY query; +static PDH_HCOUNTER counter1; +static PDH_HCOUNTER counter2; +static HANDLE mutex; + +static bool load_init (void) +{ + static NO_COPY bool tried = false; + static NO_COPY bool initialized = false; + + if (!tried) { + tried = true; + + if (!((PdhOpenQueryA (NULL, 0, &query) == ERROR_SUCCESS) && + (PdhAddEnglishCounterA (query, "\\Processor(_Total)\\% Processor Time", + 0, &counter1) == ERROR_SUCCESS) && + (PdhAddEnglishCounterA (query, "\\System\\Processor Queue Length", + 0, &counter2) == ERROR_SUCCESS))) { + debug_printf("loadavg PDH initialization failed\n"); + return false; + } + + mutex = CreateMutex(&sec_all_nih, FALSE, "cyg.loadavg.mutex"); + if (!mutex) { + debug_printf("opening loadavg mutexfailed\n"); + return false; + } + + initialized = true; + } + + return initialized; +} + +/* estimate the current load */ +static bool get_load (double *load) +{ + *load = 0.0; + + PDH_STATUS ret = PdhCollectQueryData (query); + if (ret != ERROR_SUCCESS) + return false; + + /* Estimate the number of running processes as (NumberOfProcessors) * (% + Processor Time) */ + PDH_FMT_COUNTERVALUE fmtvalue1; + ret = PdhGetFormattedCounterValue (counter1, PDH_FMT_DOUBLE, NULL, &fmtvalue1); + if (ret != ERROR_SUCCESS) + return false; + + SYSTEM_INFO sysinfo; + GetSystemInfo (&sysinfo); + + double running = fmtvalue1.doubleValue * sysinfo.dwNumberOfProcessors / 100; + + /* Estimate the number of runnable processes using ProcessorQueueLength */ + PDH_FMT_COUNTERVALUE fmtvalue2; + ret = PdhGetFormattedCounterValue (counter2, PDH_FMT_LONG, NULL, &fmtvalue2); + if (ret != ERROR_SUCCESS) + return false; + + LONG rql = fmtvalue2.longValue; + + *load = rql + running; + return true; +} + +/* + loadavginfo shared-memory object +*/ + +void loadavginfo::initialize () +{ + for (int i = 0; i < 3; i++) + loadavg[i] = 0.0; + + last_time = 0; +} + +void loadavginfo::calc_load (int index, int delta_time, int decay_time, double n) +{ + double df = 1.0 / exp ((double)delta_time/decay_time); + loadavg[index] = (loadavg[index] * df) + (n * (1.0 - df)); +} + +void loadavginfo::update_loadavg () +{ + double active_tasks; + + if (!get_load (&active_tasks)) + return; + + /* Don't recalculate the load average if less than 5 seconds has elapsed since + the last time it was calculated */ + time_t curr_time = time (NULL); + int delta_time = curr_time - last_time; + if (delta_time < 5) { + return; + } + + if (last_time == 0) { + /* Initialize the load average to the current load */ + for (int i = 0; i < 3; i++) { + loadavg[i] = active_tasks; + } + } else { + /* Compute the exponentially weighted moving average over ... */ + calc_load (0, delta_time, 60, active_tasks); /* ... 1 min */ + calc_load (1, delta_time, 300, active_tasks); /* ... 5 min */ + calc_load (2, delta_time, 900, active_tasks); /* ... 15 min */ + } + + last_time = curr_time; +} + +int loadavginfo::fetch (double _loadavg[], int nelem) +{ + if (!load_init ()) + return 0; + + WaitForSingleObject(mutex, INFINITE); + + update_loadavg (); + + memcpy (_loadavg, loadavg, nelem * sizeof(double)); + + ReleaseMutex(mutex); + + return nelem; +} + +/* getloadavg: BSD */ +extern "C" int +getloadavg (double loadavg[], int nelem) +{ + /* The maximum number of samples is 3 */ + if (nelem > 3) + nelem = 3; + + /* Return the samples and number of samples retrieved */ + return cygwin_shared->loadavg.fetch(loadavg, nelem); +} diff --git a/winsup/cygwin/loadavg.h b/winsup/cygwin/loadavg.h new file mode 100644 index 000000000..e6fb594d0 --- /dev/null +++ b/winsup/cygwin/loadavg.h @@ -0,0 +1,24 @@ +/* loadavg.h: load average support. + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#ifndef LOADAVG_H +#define LOADAVG_H + +class loadavginfo +{ + double loadavg[3]; + time_t last_time; + + public: + void initialize (); + int fetch (double _loadavg[], int nelem); + void update_loadavg (); + void calc_load (int index, int delta_time, int decay_time, double n); +}; + +#endif /* LOADAVG_H */ diff --git a/winsup/cygwin/shared.cc b/winsup/cygwin/shared.cc index 4ed4c1199..dd16f1466 100644 --- a/winsup/cygwin/shared.cc +++ b/winsup/cygwin/shared.cc @@ -328,6 +328,7 @@ shared_info::initialize () init_obcaseinsensitive (); /* Initialize obcaseinsensitive */ tty.init (); /* Initialize tty table */ mt.initialize (); /* Initialize shared tape information */ + loadavg.initialize (); /* Initialize loadavg information */ /* Defer debug output printing the installation root and installation key up to this point. Debug output except for system_printf requires the global shared memory to exist. */ diff --git a/winsup/cygwin/shared_info.h b/winsup/cygwin/shared_info.h index ce17c15ba..d03844db3 100644 --- a/winsup/cygwin/shared_info.h +++ b/winsup/cygwin/shared_info.h @@ -11,6 +11,7 @@ details. */ #include "mtinfo.h" #include "limits.h" #include "mount.h" +#include "loadavg.h" #define CURR_USER_MAGIC 0xab1fcce8U @@ -48,6 +49,7 @@ class shared_info LONG last_used_bindresvport; DWORD obcaseinsensitive; mtinfo mt; + loadavginfo loadavg; void initialize (); void init_obcaseinsensitive (); diff --git a/winsup/doc/posix.xml b/winsup/doc/posix.xml index 03d168d3e..a3b2e9f5e 100644 --- a/winsup/doc/posix.xml +++ b/winsup/doc/posix.xml @@ -1173,6 +1173,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008). getdtablesize getgrouplist getifaddrs + getloadavg getpagesize getpeereid getprogname