CPU usage parameters on Windows moved to winnt.nsm from winperf.nsm and no longer...
[public/netxms.git] / src / agent / subagents / winnt / cpu.cpp
1 /*
2 ** NetXMS platform subagent for Windows
3 ** Copyright (C) 2003-2016 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: cpu.cpp
20 **
21 **/
22
23 #include "winnt_subagent.h"
24 #include <winternl.h>
25
26 static int s_cpuCount = 0;
27 static SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *s_cpuTimes = NULL;
28 static UINT32 *s_usage = NULL;
29 static UINT32 *s_idle = NULL;
30 static UINT32 *s_kernel = NULL;
31 static UINT32 *s_user = NULL;
32 static int s_bpos = 0;
33 static CRITICAL_SECTION s_lock;
34
35 /**
36 * CPU collector thread
37 */
38 static THREAD_RESULT THREAD_CALL CPUStatCollector(void *arg)
39 {
40 nxlog_debug(3, _T("CPU stat collector started (%d CPUs)"), s_cpuCount);
41
42 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *prev = s_cpuTimes;
43 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *curr = &s_cpuTimes[s_cpuCount];
44
45 ULONG cpuTimesLen = sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * s_cpuCount;
46 ULONG size;
47 NtQuerySystemInformation(SystemProcessorPerformanceInformation, s_cpuTimes, cpuTimesLen, &size);
48
49 while(!AgentSleepAndCheckForShutdown(1000))
50 {
51 if (NtQuerySystemInformation(SystemProcessorPerformanceInformation, curr, cpuTimesLen, &size) != 0)
52 {
53 memcpy(curr, prev, cpuTimesLen);
54 }
55
56 UINT64 sysIdle = 0;
57 UINT64 sysKernel = 0;
58 UINT64 sysUser = 0;
59
60 EnterCriticalSection(&s_lock);
61
62 int idx = s_bpos;
63 for(int i = 0; i < s_cpuCount; i++, idx++)
64 {
65 UINT64 idle = curr[i].IdleTime.QuadPart - prev[i].IdleTime.QuadPart;
66 UINT64 kernel = curr[i].KernelTime.QuadPart - prev[i].KernelTime.QuadPart;
67 UINT64 user = curr[i].UserTime.QuadPart - prev[i].UserTime.QuadPart;
68 UINT64 total = kernel + user; // kernel time includes idle time
69
70 sysIdle += idle;
71 sysKernel += kernel;
72 sysUser += user;
73
74 if (total > 0)
75 {
76 s_usage[idx] = (UINT32)((total - idle) * 10000 / total);
77 s_idle[idx] = (UINT32)(idle * 10000 / total);
78 s_kernel[idx] = (UINT32)((kernel - idle) * 10000 / total);
79 s_user[idx] = (UINT32)(user * 10000 / total);
80 }
81 else
82 {
83 s_usage[idx] = 0;
84 s_idle[idx] = 0;
85 s_kernel[idx] = 0;
86 s_user[idx] = 0;
87 }
88 }
89
90 UINT64 sysTotal = sysKernel + sysUser;
91 if (sysTotal > 0)
92 {
93 s_usage[idx] = (UINT32)((sysTotal - sysIdle) * 10000 / sysTotal);
94 s_idle[idx] = (UINT32)(sysIdle * 10000 / sysTotal);
95 s_kernel[idx] = (UINT32)((sysKernel - sysIdle) * 10000 / sysTotal);
96 s_user[idx] = (UINT32)(sysUser * 10000 / sysTotal);
97 }
98 else
99 {
100 s_usage[idx] = 0;
101 s_idle[idx] = 0;
102 s_kernel[idx] = 0;
103 s_user[idx] = 0;
104 }
105
106 s_bpos += s_cpuCount + 1;
107 if (s_bpos >= (s_cpuCount + 1) * 900)
108 s_bpos = 0;
109
110 LeaveCriticalSection(&s_lock);
111
112 // swap buffers
113 if (curr == s_cpuTimes)
114 {
115 curr = prev;
116 prev = s_cpuTimes;
117 }
118 else
119 {
120 prev = curr;
121 curr = s_cpuTimes;
122 }
123 }
124 nxlog_debug(3, _T("CPU stat collector stopped"));
125 return THREAD_OK;
126 }
127
128 /**
129 * Collector thread handle
130 */
131 static THREAD s_collectorThread = INVALID_THREAD_HANDLE;
132
133 /**
134 * Start collector
135 */
136 void StartCPUStatCollector()
137 {
138 SYSTEM_INFO si;
139 GetSystemInfo(&si);
140 s_cpuCount = (int)si.dwNumberOfProcessors;
141 s_cpuTimes = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *)malloc(sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * s_cpuCount * 2);
142 s_usage = (UINT32 *)calloc(900, sizeof(UINT32) * (s_cpuCount + 1));
143 s_idle = (UINT32 *)calloc(900, sizeof(UINT32) * (s_cpuCount + 1));
144 s_kernel = (UINT32 *)calloc(900, sizeof(UINT32) * (s_cpuCount + 1));
145 s_user = (UINT32 *)calloc(900, sizeof(UINT32) * (s_cpuCount + 1));
146 InitializeCriticalSectionAndSpinCount(&s_lock, 1000);
147 s_collectorThread = ThreadCreateEx(CPUStatCollector, 0, NULL);
148 }
149
150 /**
151 * Stop collector
152 */
153 void StopCPUStatCollector()
154 {
155 ThreadJoin(s_collectorThread);
156 free(s_cpuTimes);
157 free(s_usage);
158 free(s_idle);
159 free(s_kernel);
160 free(s_user);
161 DeleteCriticalSection(&s_lock);
162 }
163
164 /**
165 * Handler for System.CPU.Usage parameters
166 */
167 LONG H_CPUUsage(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
168 {
169 int cpuIndex;
170 if (arg[0] == 'T') // Total
171 {
172 cpuIndex = s_cpuCount;
173 }
174 else
175 {
176 TCHAR buffer[64];
177 if (!AgentGetParameterArg(param, 1, buffer, 64))
178 return SYSINFO_RC_UNSUPPORTED;
179 cpuIndex = _tcstol(buffer, NULL, 10);
180 if ((cpuIndex < 0) || (cpuIndex >= s_cpuCount))
181 return SYSINFO_RC_UNSUPPORTED;
182 }
183
184 UINT32 usage = 0;
185 int step = s_cpuCount + 1;
186 int count;
187 switch(arg[1])
188 {
189 case '0':
190 count = 1;
191 break;
192 case '1':
193 count = 60;
194 break;
195 case '2':
196 count = 300;
197 break;
198 case '3':
199 count = 900;
200 break;
201 default:
202 return SYSINFO_RC_UNSUPPORTED;
203 break;
204 }
205
206 UINT32 *data;
207 switch(arg[2])
208 {
209 case 'U':
210 data = s_usage;
211 break;
212 case 'I':
213 data = s_idle;
214 break;
215 case 's':
216 data = s_kernel;
217 break;
218 case 'u':
219 data = s_user;
220 break;
221 default:
222 return SYSINFO_RC_UNSUPPORTED;
223 break;
224 }
225
226 EnterCriticalSection(&s_lock);
227 for(int p = s_bpos - step, i = 0; i < count; i++, p -= step)
228 {
229 if (p < 0)
230 p = s_cpuCount * 900 - step;
231 usage += data[p + cpuIndex];
232 }
233 LeaveCriticalSection(&s_lock);
234
235 usage /= count;
236 _sntprintf(value, MAX_RESULT_LENGTH, _T("%d.%02d"), usage / 100, usage % 100);
237 return SYSINFO_RC_SUCCESS;
238 }