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:
parent
b568f92c50
commit
d0a359f6d2
|
@ -323,6 +323,7 @@ DLL_OFILES:= \
|
|||
kernel32.o \
|
||||
ldap.o \
|
||||
libstdcxx_wrapper.o \
|
||||
loadavg.o \
|
||||
localtime.o \
|
||||
lsearch.o \
|
||||
malloc_wrapper.o \
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -624,6 +624,7 @@ gethostname = cygwin_gethostname SIGFE
|
|||
getifaddrs SIGFE
|
||||
getitimer SIGFE
|
||||
getline = __getline SIGFE
|
||||
getloadavg SIGFE
|
||||
getlogin NOSIGFE
|
||||
getlogin_r NOSIGFE
|
||||
getmntent SIGFE
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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. */
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -1173,6 +1173,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
|
|||
getdtablesize
|
||||
getgrouplist
|
||||
getifaddrs
|
||||
getloadavg
|
||||
getpagesize
|
||||
getpeereid
|
||||
getprogname
|
||||
|
|
Loading…
Reference in New Issue