Welcome to mirror list, hosted at ThFree Co, Russian Federation.

loadavg.cc « cygwin « winsup - cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 127591a2e1f5ed2f64e0b53389624264aba72c15 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/* 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>
#include <pdh.h>

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;

    PDH_STATUS status;

    status = PdhOpenQueryW (NULL, 0, &query);
    if (status != STATUS_SUCCESS)
      {
	debug_printf ("PdhOpenQueryW, status %y", status);
	return false;
      }
    status = PdhAddEnglishCounterW (query,
				    L"\\Processor(_Total)\\% Processor Time",
				    0, &counter1);
    if (status != STATUS_SUCCESS)
      {
	debug_printf ("PdhAddEnglishCounterW(time), status %y", status);
	return false;
      }
    status = PdhAddEnglishCounterW (query,
				    L"\\System\\Processor Queue Length",
				    0, &counter2);

    if (status != STATUS_SUCCESS)
      {
	debug_printf ("PdhAddEnglishCounterW(queue length), status %y", status);
	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;

  double running = fmtvalue1.doubleValue * wincap.cpu_count () / 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);
}