Implement getloadavg()

v2:
autoload PerfDataHelper functions
Keep loadavg in shared memory
Guard loadavg access by a mutex
Initialize loadavg to the current load

v3:
Shared memory version bump isn't needed if we are only extending it
Remove unused autoload
Mark inititalized flags as NO_COPY for correct behaviour in fork child

Signed-off-by: Jon Turney <jon.turney@dronecode.org.uk>
This commit is contained in:
Jon Turney 2017-03-21 19:17:42 +00:00
parent b568f92c50
commit d0a359f6d2
11 changed files with 240 additions and 4 deletions

View File

@ -323,6 +323,7 @@ DLL_OFILES:= \
kernel32.o \
ldap.o \
libstdcxx_wrapper.o \
loadavg.o \
localtime.o \
lsearch.o \
malloc_wrapper.o \

View File

@ -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)
}

View File

@ -624,6 +624,7 @@ gethostname = cygwin_gethostname SIGFE
getifaddrs SIGFE
getitimer SIGFE
getline = __getline SIGFE
getloadavg SIGFE
getlogin NOSIGFE
getlogin_r NOSIGFE
getmntent SIGFE

View File

@ -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

View File

@ -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

View File

@ -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

192
winsup/cygwin/loadavg.cc Normal file
View File

@ -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 <math.h>
#include <time.h>
#include <sys/strace.h>
/* Prototype for PdhAddEnglishCounterA in pdh.h under _WIN32_WINNT >= 0x0600 is
missing WINAPI */
#undef _WIN32_WINNT
#include <pdh.h>
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);
}

24
winsup/cygwin/loadavg.h Normal file
View File

@ -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 */

View File

@ -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. */

View File

@ -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 ();

View File

@ -1173,6 +1173,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
getdtablesize
getgrouplist
getifaddrs
getloadavg
getpagesize
getpeereid
getprogname