fixed bug in CPU info parameters handler
[public/netxms.git] / src / agent / subagents / linux / cpu.cpp
CommitLineData
9944a2c9
VK
1/*
2** NetXMS subagent for GNU/Linux
3** Copyright (C) 2004 - 2007 Alex 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**/
20
21#include "linux_subagent.h"
22
9944a2c9
VK
23#define CPU_USAGE_SLOTS 900 // 60 sec * 15 min => 900 sec
24// 64 + 1 for overal
25#define MAX_CPU (64 + 1)
26
27static THREAD m_cpuUsageCollector = INVALID_THREAD_HANDLE;
28static MUTEX m_cpuUsageMutex = INVALID_MUTEX_HANDLE;
29static bool volatile m_stopCollectorThread = false;
6e383343
VK
30static UINT64 m_user[MAX_CPU];
31static UINT64 m_nice[MAX_CPU];
32static UINT64 m_system[MAX_CPU];
33static UINT64 m_idle[MAX_CPU];
34static UINT64 m_iowait[MAX_CPU];
35static UINT64 m_irq[MAX_CPU];
36static UINT64 m_softirq[MAX_CPU];
37static UINT64 m_steal[MAX_CPU];
38static UINT64 m_guest[MAX_CPU];
9944a2c9
VK
39static float *m_cpuUsage;
40static float *m_cpuUsageUser;
41static float *m_cpuUsageNice;
42static float *m_cpuUsageSystem;
43static float *m_cpuUsageIdle;
44static float *m_cpuUsageIoWait;
45static float *m_cpuUsageIrq;
46static float *m_cpuUsageSoftIrq;
47static float *m_cpuUsageSteal;
48static float *m_cpuUsageGuest;
49static int m_currentSlot = 0;
50static int m_maxCPU = 0;
51
41580924
VK
52/**
53 * CPU usage collector
54 */
9944a2c9
VK
55static void CpuUsageCollector()
56{
57 FILE *hStat = fopen("/proc/stat", "r");
58
59 if (hStat == NULL)
60 {
41580924 61 AgentWriteDebugLog(2, _T("Cannot open /proc/stat"));
9944a2c9
VK
62 return;
63 }
64
6e383343
VK
65 UINT64 user, nice, system, idle;
66 UINT64 iowait = 0, irq = 0, softirq = 0; // 2.6
67 UINT64 steal = 0; // 2.6.11
68 UINT64 guest = 0; // 2.6.24
9944a2c9
VK
69 unsigned int cpu = 0;
70 unsigned int maxCpu = 0;
71 char buffer[1024];
72
4cdb7f66 73 MutexLock(m_cpuUsageMutex);
9944a2c9
VK
74 if (m_currentSlot == CPU_USAGE_SLOTS)
75 {
76 m_currentSlot = 0;
77 }
78
79 // scan for all CPUs
c88761c1 80 while(true)
9944a2c9
VK
81 {
82 if (fgets(buffer, sizeof(buffer), hStat) == NULL)
83 break;
84
85 if (buffer[0] != 'c' || buffer[1] != 'p' || buffer[2] != 'u')
86 continue;
87
88 int ret;
89 if (buffer[3] == ' ')
90 {
91 // "cpu ..." - Overal
92 cpu = 0;
93 ret = sscanf(buffer, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
94 &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, &guest);
95 }
96 else
97 {
98 ret = sscanf(buffer, "cpu%u %llu %llu %llu %llu %llu %llu %llu %llu %llu",
99 &cpu, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, &guest);
100 cpu++;
101 }
102
103 if (ret < 4)
104 continue;
105
106 maxCpu = max(cpu, maxCpu);
107
6e383343
VK
108 UINT64 userDelta, niceDelta, systemDelta, idleDelta;
109 UINT64 iowaitDelta, irqDelta, softirqDelta, stealDelta;
110 UINT64 guestDelta;
9944a2c9 111
c88761c1 112#define DELTA(x, y) (((x) > (y)) ? ((x) - (y)) : 0)
1e02c485
VK
113 userDelta = DELTA(user, m_user[cpu]);
114 niceDelta = DELTA(nice, m_nice[cpu]);
115 systemDelta = DELTA(system, m_system[cpu]);
116 idleDelta = DELTA(idle, m_idle[cpu]);
117 iowaitDelta = DELTA(iowait, m_iowait[cpu]);
118 irqDelta = DELTA(irq, m_irq[cpu]);
119 softirqDelta = DELTA(softirq, m_softirq[cpu]);
120 stealDelta = DELTA(steal, m_steal[cpu]); // steal=time spent in virtualization stuff (xen).
121 guestDelta = DELTA(guest, m_guest[cpu]); //
122#undef DELTA
9944a2c9 123
6e383343 124 UINT64 totalDelta = userDelta + niceDelta + systemDelta + idleDelta + iowaitDelta + irqDelta + softirqDelta + stealDelta + guestDelta;
9944a2c9
VK
125 float onePercent = (float)totalDelta / 100.0; // 1% of total
126 if (onePercent == 0)
127 {
128 onePercent = 1; // TODO: why 1?
129 }
130
131 /* update detailed stats */
132#define UPDATE(delta, target) { \
133 if (delta > 0) { *(target + (cpu * CPU_USAGE_SLOTS) + m_currentSlot) = (float)delta / onePercent; } \
134 else { *(target + (cpu * CPU_USAGE_SLOTS) + m_currentSlot) = 0; } \
135 }
136
137 UPDATE(userDelta, m_cpuUsageUser);
138 UPDATE(niceDelta, m_cpuUsageNice);
139 UPDATE(systemDelta, m_cpuUsageSystem);
140 UPDATE(idleDelta, m_cpuUsageIdle);
141 UPDATE(iowaitDelta, m_cpuUsageIoWait);
142 UPDATE(irqDelta, m_cpuUsageIrq);
143 UPDATE(softirqDelta, m_cpuUsageSoftIrq);
144 UPDATE(stealDelta, m_cpuUsageSteal);
145 UPDATE(guestDelta, m_cpuUsageGuest);
146
147 /* update overal cpu usage */
148 if (totalDelta > 0)
149 {
150 *(m_cpuUsage + (cpu * CPU_USAGE_SLOTS) + m_currentSlot) = 100.0 - ((float)idleDelta / onePercent);
151 }
152 else
153 {
154 *(m_cpuUsage + (cpu * CPU_USAGE_SLOTS) + m_currentSlot) = 0;
155 }
156
157 m_user[cpu] = user;
158 m_nice[cpu] = nice;
159 m_system[cpu] = system;
160 m_idle[cpu] = idle;
161 m_iowait[cpu] = iowait;
162 m_irq[cpu] = irq;
163 m_softirq[cpu] = softirq;
164 m_steal[cpu] = steal;
165 m_guest[cpu] = guest;
166 }
167
168 /* go to the next slot */
169 m_currentSlot++;
170 MutexUnlock(m_cpuUsageMutex);
171
172 fclose(hStat);
906fa251 173 m_maxCPU = maxCpu;
9944a2c9
VK
174}
175
41580924
VK
176/**
177 * CPU usage collector thread
178 */
9944a2c9
VK
179static THREAD_RESULT THREAD_CALL CpuUsageCollectorThread(void *pArg)
180{
9944a2c9
VK
181 while(m_stopCollectorThread == false)
182 {
183 CpuUsageCollector();
184 ThreadSleepMs(1000); // sleep 1 second
185 }
9944a2c9
VK
186 return THREAD_OK;
187}
188
41580924
VK
189/**
190 * Start CPU usage collector
191 */
192void StartCpuUsageCollector()
9944a2c9
VK
193{
194 int i, j;
195
196 m_cpuUsageMutex = MutexCreate();
197
198#define SIZE sizeof(float) * CPU_USAGE_SLOTS * MAX_CPU
199#define ALLOCATE_AND_CLEAR(x) x = (float *)malloc(SIZE); memset(x, 0, SIZE);
200 ALLOCATE_AND_CLEAR(m_cpuUsage);
201 ALLOCATE_AND_CLEAR(m_cpuUsageUser);
202 ALLOCATE_AND_CLEAR(m_cpuUsageNice);
203 ALLOCATE_AND_CLEAR(m_cpuUsageSystem);
204 ALLOCATE_AND_CLEAR(m_cpuUsageIdle);
205 ALLOCATE_AND_CLEAR(m_cpuUsageIoWait);
206 ALLOCATE_AND_CLEAR(m_cpuUsageIrq);
207 ALLOCATE_AND_CLEAR(m_cpuUsageSoftIrq);
208 ALLOCATE_AND_CLEAR(m_cpuUsageSteal);
209 ALLOCATE_AND_CLEAR(m_cpuUsageGuest);
210#undef ALLOCATE_AND_CLEAR
211#undef SIZE
212
6e383343 213#define CLEAR(x) memset(x, 0, sizeof(UINT64) * MAX_CPU);
9944a2c9
VK
214 CLEAR(m_user)
215 CLEAR(m_nice)
216 CLEAR(m_system)
217 CLEAR(m_idle)
218 CLEAR(m_iowait)
219 CLEAR(m_irq)
220 CLEAR(m_softirq)
221 CLEAR(m_steal)
222 CLEAR(m_guest)
223#undef CLEAR
224
225 // get initial count of user/system/idle time
226 m_currentSlot = 0;
227 CpuUsageCollector();
228
229 sleep(1);
230
231 // fill first slot with u/s/i delta
232 m_currentSlot = 0;
233 CpuUsageCollector();
234
235 // fill all slots with current cpu usage
236#define FILL(x) memcpy(x + i, x, sizeof(float));
237 for (i = 0; i < (CPU_USAGE_SLOTS * MAX_CPU) - 1; i++)
238 {
239 FILL(m_cpuUsage);
240 FILL(m_cpuUsageUser);
241 FILL(m_cpuUsageNice);
242 FILL(m_cpuUsageSystem);
243 FILL(m_cpuUsageIdle);
244 FILL(m_cpuUsageIoWait);
245 FILL(m_cpuUsageIrq);
246 FILL(m_cpuUsageSoftIrq);
247 FILL(m_cpuUsageSteal);
248 FILL(m_cpuUsageGuest);
249 }
250#undef FILL
251
252 // start collector
253 m_cpuUsageCollector = ThreadCreateEx(CpuUsageCollectorThread, 0, NULL);
254}
255
41580924
VK
256/**
257 * Shutdown CPU usage collector
258 */
259void ShutdownCpuUsageCollector()
9944a2c9
VK
260{
261 m_stopCollectorThread = true;
262 ThreadJoin(m_cpuUsageCollector);
263 MutexDestroy(m_cpuUsageMutex);
264
265 free(m_cpuUsage);
266 free(m_cpuUsageUser);
267 free(m_cpuUsageNice);
268 free(m_cpuUsageSystem);
269 free(m_cpuUsageIdle);
270 free(m_cpuUsageIoWait);
271 free(m_cpuUsageIrq);
272 free(m_cpuUsageSoftIrq);
273 free(m_cpuUsageSteal);
274 free(m_cpuUsageGuest);
275}
276
f3387429 277static void GetUsage(int source, int cpu, int count, TCHAR *value)
9944a2c9
VK
278{
279 float *table;
280 switch (source)
281 {
282 case CPU_USAGE_OVERAL:
283 table = (float *)m_cpuUsage;
284 break;
285 case CPU_USAGE_USER:
286 table = (float *)m_cpuUsageUser;
287 break;
288 case CPU_USAGE_NICE:
289 table = (float *)m_cpuUsageNice;
290 break;
291 case CPU_USAGE_SYSTEM:
292 table = (float *)m_cpuUsageSystem;
293 break;
294 case CPU_USAGE_IDLE:
295 table = (float *)m_cpuUsageIdle;
296 break;
297 case CPU_USAGE_IOWAIT:
298 table = (float *)m_cpuUsageIoWait;
299 break;
300 case CPU_USAGE_IRQ:
301 table = (float *)m_cpuUsageIrq;
302 break;
303 case CPU_USAGE_SOFTIRQ:
304 table = (float *)m_cpuUsageSoftIrq;
305 break;
306 case CPU_USAGE_STEAL:
307 table = (float *)m_cpuUsageSteal;
308 break;
309 case CPU_USAGE_GUEST:
310 table = (float *)m_cpuUsageGuest;
311 break;
312 default:
313 table = (float *)m_cpuUsage;
314 }
315
316 table += cpu * CPU_USAGE_SLOTS;
317
318 float usage = 0;
9944a2c9 319
4cdb7f66 320 MutexLock(m_cpuUsageMutex);
2047c0ea
VK
321
322 float *p = table + m_currentSlot - 1;
323 for (int i = 0; i < count; i++)
324 {
9944a2c9 325 usage += *p;
2047c0ea
VK
326 if (p == table)
327 {
9944a2c9
VK
328 p += CPU_USAGE_SLOTS;
329 }
330 p--;
331 }
332
333 MutexUnlock(m_cpuUsageMutex);
334
335 usage /= count;
336 ret_double(value, usage);
337}
338
060c5a11 339LONG H_CpuUsage(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session)
9944a2c9
VK
340{
341 int count;
342
343 switch(CPU_USAGE_PARAM_INTERVAL(pArg))
344 {
345 case INTERVAL_5MIN:
346 count = 5 * 60;
347 break;
348 case INTERVAL_15MIN:
349 count = 15 * 60;
350 break;
351 default:
352 count = 60;
353 break;
354 }
355
356 GetUsage(CPU_USAGE_PARAM_SOURCE(pArg), 0, count, pValue);
357 return SYSINFO_RC_SUCCESS;
358}
359
060c5a11 360LONG H_CpuUsageEx(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session)
9944a2c9
VK
361{
362 int count, cpu;
f3387429 363 TCHAR buffer[256], *eptr;
9944a2c9
VK
364 struct CpuUsageParam *p = (struct CpuUsageParam *)pValue;
365
366 if (!AgentGetParameterArg(pszParam, 1, buffer, 256))
367 return SYSINFO_RC_UNSUPPORTED;
368
f3387429 369 cpu = _tcstol(buffer, &eptr, 0);
9944a2c9
VK
370 if ((*eptr != 0) || (cpu < 0) || (cpu >= m_maxCPU))
371 return SYSINFO_RC_UNSUPPORTED;
372
373 switch(CPU_USAGE_PARAM_INTERVAL(pArg))
374 {
375 case INTERVAL_5MIN:
376 count = 5 * 60;
377 break;
378 case INTERVAL_15MIN:
379 count = 15 * 60;
380 break;
381 default:
382 count = 60;
383 break;
384 }
385
386 GetUsage(CPU_USAGE_PARAM_SOURCE(pArg), cpu + 1, count, pValue);
387
388 return SYSINFO_RC_SUCCESS;
389}
390
41580924
VK
391/**
392 * Handler for System.CPU.Count parameter
393 */
060c5a11 394LONG H_CpuCount(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue, AbstractCommSession *session)
9944a2c9 395{
906fa251 396 ret_uint(pValue, m_maxCPU);
9944a2c9
VK
397 return SYSINFO_RC_SUCCESS;
398}
41580924
VK
399
400/**
401 * CPU info structure
402 */
403struct CPU_INFO
404{
405 int id;
406 int coreId;
407 int physicalId;
408 char model[64];
409 long frequency;
410 int cacheSize;
411};
412
413/**
414 * Read /proc/cpuinfo
415 */
416static int ReadCpuInfo(CPU_INFO *info, int size)
417{
418 FILE *f = fopen("/proc/cpuinfo", "r");
419 if (f == NULL)
420 {
421 AgentWriteDebugLog(2, _T("Cannot open /proc/cpuinfo"));
422 return -1;
423 }
424
425 int count = -1;
426 char buffer[256];
427 while(!feof(f))
428 {
429 if (fgets(buffer, sizeof(buffer), f) == NULL)
430 break;
431 char *s = strchr(buffer, '\n');
432 if (s != NULL)
433 *s = 0;
434
435 s = strchr(buffer, ':');
436 if (s == NULL)
437 continue;
438
439 *s = 0;
440 s++;
441 StrStripA(buffer);
442 StrStripA(s);
443
444 if (!strcmp(buffer, "processor"))
445 {
446 count++;
447 memset(&info[count], 0, sizeof(CPU_INFO));
448 info[count].id = (int)strtol(s, NULL, 10);
449 continue;
450 }
451
452 if (count == -1)
453 continue;
454
455 if (!strcmp(buffer, "model name"))
456 {
457 strncpy(info[count].model, s, 63);
458 }
459 else if (!strcmp(buffer, "cpu MHz"))
460 {
461 char *eptr;
462 info[count].frequency = strtol(s, &eptr, 10) * 1000;
463 if (*eptr == '.')
464 {
465 eptr[4] = 0;
466 info[count].frequency += strtol(eptr + 1, NULL, 10);
467 }
468 }
469 else if (!strcmp(buffer, "cache size"))
470 {
471 info[count].cacheSize = (int)strtol(s, NULL, 10);
472 }
473 else if (!strcmp(buffer, "physical id"))
474 {
475 info[count].physicalId = (int)strtol(s, NULL, 10);
476 }
477 else if (!strcmp(buffer, "core id"))
478 {
479 info[count].coreId = (int)strtol(s, NULL, 10);
480 }
481 }
482
483 fclose(f);
a3c8e5e6 484 return count + 1;
41580924
VK
485}
486
487/**
488 * Handler for CPU info parameters
489 */
490LONG H_CpuInfo(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
491{
492 CPU_INFO cpuInfo[256];
493 int count = ReadCpuInfo(cpuInfo, 256);
494 if (count <= 0)
495 return SYSINFO_RC_ERROR;
496
497 TCHAR buffer[32];
498 AgentGetParameterArg(param, 1, buffer, 32);
499 int cpuId = (int)_tcstol(buffer, NULL, 0);
500
501 CPU_INFO *cpu = NULL;
502 for(int i = 0; i < count; i++)
503 {
504 if (cpuInfo[i].id == cpuId)
505 {
506 cpu = &cpuInfo[i];
507 break;
508 }
509 }
510 if (cpu == NULL)
511 return SYSINFO_RC_NO_SUCH_INSTANCE;
512
513 switch(*arg)
514 {
515 case 'C': // Core ID
516 ret_int(value, cpu->coreId);
517 break;
518 case 'F': // Frequency
519 _sntprintf(value, MAX_RESULT_LENGTH, _T("%d.%03d"), cpu->frequency / 1000, cpu->frequency % 1000);
520 break;
521 case 'M': // Model
522 ret_mbstring(value, cpu->model);
523 break;
524 case 'P': // Physical ID
525 ret_int(value, cpu->physicalId);
526 break;
527 case 'S': // Cache size
528 ret_int(value, cpu->cacheSize);
529 break;
530 default:
531 return SYSINFO_RC_UNSUPPORTED;
532 }
533
534 return SYSINFO_RC_SUCCESS;
535}