#include <stddef.h> /* for offsetof */
#include <stdlib.h>
#include <stdio.h>
+#include <float.h> /* for DBL_EPSILON */
#include <io.h>
#include <errno.h>
#include <fcntl.h>
static BOOL g_b_init_get_length_sid;
static BOOL g_b_init_equal_sid;
static BOOL g_b_init_copy_sid;
+static BOOL g_b_init_get_native_system_info;
+static BOOL g_b_init_get_system_times;
/*
BEGIN: Wrapper functions around OpenProcessToken
PSID pSid2);
typedef DWORD (WINAPI * GetLengthSid_Proc) (
PSID pSid);
+typedef void (WINAPI * GetNativeSystemInfo_Proc) (
+ LPSYSTEM_INFO lpSystemInfo);
+typedef BOOL (WINAPI * GetSystemTimes_Proc) (
+ LPFILETIME lpIdleTime,
+ LPFILETIME lpKernelTime,
+ LPFILETIME lpUserTime);
supported in Windows NT / 2k / XP
*/
+void WINAPI get_native_system_info (
+ LPSYSTEM_INFO lpSystemInfo)
+{
+ static GetNativeSystemInfo_Proc s_pfn_Get_Native_System_Info = NULL;
+ if (is_windows_9x () != TRUE)
+ {
+ if (g_b_init_get_native_system_info == 0)
+ {
+ g_b_init_get_native_system_info = 1;
+ s_pfn_Get_Native_System_Info =
+ (GetNativeSystemInfo_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+ "GetNativeSystemInfo");
+ }
+ if (s_pfn_Get_Native_System_Info != NULL)
+ s_pfn_Get_Native_System_Info (lpSystemInfo);
+ }
+ else
+ lpSystemInfo->dwNumberOfProcessors = -1;
+}
+
+BOOL WINAPI get_system_times(
+ LPFILETIME lpIdleTime,
+ LPFILETIME lpKernelTime,
+ LPFILETIME lpUserTime)
+{
+ static GetSystemTimes_Proc s_pfn_Get_System_times = NULL;
+ if (is_windows_9x () == TRUE)
+ {
+ return FALSE;
+ }
+ if (g_b_init_get_system_times == 0)
+ {
+ g_b_init_get_system_times = 1;
+ s_pfn_Get_System_times =
+ (GetSystemTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+ "GetSystemTimes");
+ }
+ if (s_pfn_Get_System_times == NULL)
+ return FALSE;
+ return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime));
+}
\f
/* Equivalent of strerror for W32 error codes. */
char *
#endif /* HAVE_SOCKETS */
/* Emulate getloadavg. */
+
+struct load_sample {
+ time_t sample_time;
+ ULONGLONG idle;
+ ULONGLONG kernel;
+ ULONGLONG user;
+};
+
+/* Number of processors on this machine. */
+static unsigned num_of_processors;
+
+/* We maintain 1-sec samples for the last 16 minutes in a circular buffer. */
+static struct load_sample samples[16*60];
+static int first_idx = -1, last_idx = -1;
+static int max_idx = sizeof (samples) / sizeof (samples[0]);
+
+static int
+buf_next (int from)
+{
+ int next_idx = from + 1;
+
+ if (next_idx >= max_idx)
+ next_idx = 0;
+
+ return next_idx;
+}
+
+static int
+buf_prev (int from)
+{
+ int prev_idx = from - 1;
+
+ if (prev_idx < 0)
+ prev_idx = max_idx - 1;
+
+ return prev_idx;
+}
+
+static void
+sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user)
+{
+ SYSTEM_INFO sysinfo;
+ FILETIME ft_idle, ft_user, ft_kernel;
+
+ /* Initialize the number of processors on this machine. */
+ if (num_of_processors <= 0)
+ {
+ get_native_system_info (&sysinfo);
+ num_of_processors = sysinfo.dwNumberOfProcessors;
+ if (num_of_processors <= 0)
+ {
+ GetSystemInfo (&sysinfo);
+ num_of_processors = sysinfo.dwNumberOfProcessors;
+ }
+ if (num_of_processors <= 0)
+ num_of_processors = 1;
+ }
+
+ /* TODO: Take into account threads that are ready to run, by
+ sampling the "\System\Processor Queue Length" performance
+ counter. The code below accounts only for threads that are
+ actually running. */
+
+ if (get_system_times (&ft_idle, &ft_kernel, &ft_user))
+ {
+ ULARGE_INTEGER uidle, ukernel, uuser;
+
+ memcpy (&uidle, &ft_idle, sizeof (ft_idle));
+ memcpy (&ukernel, &ft_kernel, sizeof (ft_kernel));
+ memcpy (&uuser, &ft_user, sizeof (ft_user));
+ *idle = uidle.QuadPart;
+ *kernel = ukernel.QuadPart;
+ *user = uuser.QuadPart;
+ }
+ else
+ {
+ *idle = 0;
+ *kernel = 0;
+ *user = 0;
+ }
+}
+
+/* Produce the load average for a given time interval, using the
+ samples in the samples[] array. WHICH can be 0, 1, or 2, meaning
+ 1-minute, 5-minute, or 15-minute average, respectively. */
+static double
+getavg (int which)
+{
+ double retval = -1.0;
+ double tdiff;
+ int idx;
+ double span = (which == 0 ? 1.0 : (which == 1 ? 5.0 : 15.0)) * 60;
+ time_t now = samples[last_idx].sample_time;
+
+ if (first_idx != last_idx)
+ {
+ for (idx = buf_prev (last_idx); ; idx = buf_prev (idx))
+ {
+ tdiff = difftime (now, samples[idx].sample_time);
+ if (tdiff >= span - 2*DBL_EPSILON*now)
+ {
+ long double sys =
+ samples[last_idx].kernel + samples[last_idx].user
+ - (samples[idx].kernel + samples[idx].user);
+ long double idl = samples[last_idx].idle - samples[idx].idle;
+
+ retval = (1.0 - idl / sys) * num_of_processors;
+ break;
+ }
+ if (idx == first_idx)
+ break;
+ }
+ }
+
+ return retval;
+}
+
int
getloadavg (double loadavg[], int nelem)
{
- int i;
+ int elem;
+ ULONGLONG idle, kernel, user;
+ time_t now = time (NULL);
+
+ /* Store another sample. We ignore samples that are less than 1 sec
+ apart. */
+ if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now)
+ {
+ sample_system_load (&idle, &kernel, &user);
+ last_idx = buf_next (last_idx);
+ samples[last_idx].sample_time = now;
+ samples[last_idx].idle = idle;
+ samples[last_idx].kernel = kernel;
+ samples[last_idx].user = user;
+ /* If the buffer has more that 15 min worth of samples, discard
+ the old ones. */
+ if (first_idx == -1)
+ first_idx = last_idx;
+ while (first_idx != last_idx
+ && (difftime (now, samples[first_idx].sample_time)
+ >= 15.0*60 + 2*DBL_EPSILON*now))
+ first_idx = buf_next (first_idx);
+ }
- /* A faithful emulation is going to have to be saved for a rainy day. */
- for (i = 0; i < nelem; i++)
+ for (elem = 0; elem < nelem; elem++)
{
- loadavg[i] = 0.0;
+ double avg = getavg (elem);
+
+ if (avg < 0)
+ break;
+ loadavg[elem] = avg;
}
- return i;
+
+ return elem;
}
/* Emulate getpwuid, getpwnam and others. */
g_b_init_equal_sid = 0;
g_b_init_copy_sid = 0;
g_b_init_get_length_sid = 0;
+ g_b_init_get_native_system_info = 0;
+ g_b_init_get_system_times = 0;
+ num_of_processors = 0;
/* The following sets a handler for shutdown notifications for
console apps. This actually applies to Emacs in both console and
GUI modes, since we had to fool windows into thinking emacs is a