1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "stats.h"
22 #include "perfstat.h"
23 #include "alias.h"
24 #include "log.h"
25 #include "mutexs.h"
26 #include "sysinfo.h"
27
28 #define OBJECT_CACHE_REFRESH_INTERVAL 60
29 #define NAMES_UPDATE_INTERVAL 60
30
31 struct object_name_ref
32 {
33 char *eng_name;
34 wchar_t *loc_name;
35 };
36
37 typedef struct
38 {
39 zbx_perf_counter_data_t *pPerfCounterList;
40 PDH_HQUERY pdh_query;
41 time_t lastrefresh_objects; /* last refresh time of object cache */
42 time_t lastupdate_names; /* last update time of object names */
43 }
44 ZBX_PERF_STAT_DATA;
45
46 static ZBX_PERF_STAT_DATA ppsd;
47 static zbx_mutex_t perfstat_access = ZBX_MUTEX_NULL;
48
49 static struct object_name_ref *object_names = NULL;
50 static int object_num = 0;
51
52 #define LOCK_PERFCOUNTERS zbx_mutex_lock(perfstat_access)
53 #define UNLOCK_PERFCOUNTERS zbx_mutex_unlock(perfstat_access)
54
perf_collector_started(void)55 static int perf_collector_started(void)
56 {
57 return (NULL != ppsd.pdh_query ? SUCCEED : FAIL);
58 }
59
60 /******************************************************************************
61 * *
62 * Comments: counter failed or disappeared, dismiss all previous values *
63 * *
64 ******************************************************************************/
deactivate_perf_counter(zbx_perf_counter_data_t * counter)65 static void deactivate_perf_counter(zbx_perf_counter_data_t *counter)
66 {
67 zabbix_log(LOG_LEVEL_DEBUG, "deactivate_perf_counter() counterpath:'%s'", counter->counterpath);
68
69 counter->status = PERF_COUNTER_NOTSUPPORTED;
70 counter->value_count = 0;
71 counter->value_current = -1;
72 counter->olderRawValue = 0;
73 counter->sum = 0;
74 }
75
76 /******************************************************************************
77 * *
78 * Comments: if the specified counter exists or a new is successfully *
79 * added, a pointer to that counter is returned, NULL otherwise *
80 * *
81 ******************************************************************************/
add_perf_counter(const char * name,const char * counterpath,int interval,zbx_perf_counter_lang_t lang,char ** error)82 zbx_perf_counter_data_t *add_perf_counter(const char *name, const char *counterpath, int interval,
83 zbx_perf_counter_lang_t lang, char **error)
84 {
85 zbx_perf_counter_data_t *cptr = NULL;
86 PDH_STATUS pdh_status;
87 int added = FAIL;
88
89 zabbix_log(LOG_LEVEL_DEBUG, "In %s() counter:'%s' interval:%d", __func__, counterpath, interval);
90
91 LOCK_PERFCOUNTERS;
92
93 if (SUCCEED != perf_collector_started())
94 {
95 *error = zbx_strdup(*error, "Performance collector is not started.");
96 goto out;
97 }
98
99 for (cptr = ppsd.pPerfCounterList; ; cptr = cptr->next)
100 {
101 /* add new parameters */
102 if (NULL == cptr)
103 {
104 cptr = (zbx_perf_counter_data_t *)zbx_malloc(cptr, sizeof(zbx_perf_counter_data_t));
105
106 /* initialize the counter */
107 memset(cptr, 0, sizeof(zbx_perf_counter_data_t));
108 if (NULL != name)
109 cptr->name = zbx_strdup(NULL, name);
110 cptr->counterpath = zbx_strdup(NULL, counterpath);
111 cptr->interval = interval;
112 cptr->lang = lang;
113 cptr->value_current = -1;
114 cptr->value_array = (double *)zbx_malloc(cptr->value_array, sizeof(double) * interval);
115
116 /* add the counter to the query */
117 pdh_status = zbx_PdhAddCounter(__func__, cptr, ppsd.pdh_query, counterpath,
118 lang, &cptr->handle);
119
120 cptr->next = ppsd.pPerfCounterList;
121 ppsd.pPerfCounterList = cptr;
122
123 if (ERROR_SUCCESS != pdh_status && PDH_CSTATUS_NO_INSTANCE != pdh_status)
124 {
125 *error = zbx_dsprintf(*error, "Invalid performance counter format.");
126 cptr = NULL; /* indicate a failure */
127 }
128
129 added = SUCCEED;
130 break;
131 }
132
133 if (NULL != name)
134 {
135 if (0 == strcmp(cptr->name, name))
136 break;
137 }
138 else if (0 == strcmp(cptr->counterpath, counterpath) &&
139 cptr->interval == interval && cptr->lang == lang)
140 {
141 break;
142 }
143 }
144
145 if (FAIL == added)
146 {
147 zabbix_log(LOG_LEVEL_DEBUG, "%s() counter '%s' already exists", __func__, counterpath);
148 }
149 else if (NULL != name && NULL != cptr)
150 {
151 char *alias_name;
152
153 alias_name = zbx_dsprintf(NULL, "__UserPerfCounter[%s]", name);
154 add_alias(name, alias_name);
155 zbx_free(alias_name);
156 }
157 out:
158 UNLOCK_PERFCOUNTERS;
159
160 zabbix_log(LOG_LEVEL_DEBUG, "End of %s(): %s", __func__, NULL == cptr ? "FAIL" : "SUCCEED");
161
162 return cptr;
163 }
164
165 /******************************************************************************
166 * *
167 * Function: extend_perf_counter_interval *
168 * *
169 * Purpose: extends the performance counter buffer to store the new data *
170 * interval *
171 * *
172 * Parameters: result - [IN] the performance counter *
173 * interval - [IN] the new data collection interval in seconds *
174 * *
175 ******************************************************************************/
extend_perf_counter_interval(zbx_perf_counter_data_t * counter,int interval)176 static void extend_perf_counter_interval(zbx_perf_counter_data_t *counter, int interval)
177 {
178 if (interval <= counter->interval)
179 return;
180
181 counter->value_array = (double *)zbx_realloc(counter->value_array, sizeof(double) * interval);
182
183 /* move the data to the end to keep the ring buffer intact */
184 if (counter->value_current < counter->value_count)
185 {
186 int i;
187 double *src, *dst;
188
189 src = &counter->value_array[counter->interval - 1];
190 dst = &counter->value_array[interval - 1];
191
192 for (i = 0; i < counter->value_count - counter->value_current; i++)
193 *dst-- = *src--;
194 }
195
196 counter->interval = interval;
197 }
198
free_object_names(void)199 static void free_object_names(void)
200 {
201 int i;
202
203 for (i = 0; i < object_num; i++)
204 {
205 zbx_free(object_names[i].eng_name);
206 zbx_free(object_names[i].loc_name);
207 }
208
209 zbx_free(object_names);
210 object_num = 0;
211 }
212
213 /******************************************************************************
214 * *
215 * Function: set_object_names *
216 * *
217 * Purpose: obtains PDH object localized names and associates them with *
218 * English names, to be used by perf_instance_en.discovery *
219 * *
220 * Return value: SUCCEED/FAIL *
221 * *
222 ******************************************************************************/
set_object_names(void)223 static int set_object_names(void)
224 {
225 wchar_t *names_eng, *names_loc, *eng_name, *loc_name, *objects, *object, *p_eng = NULL, *p_loc = NULL;
226 DWORD sz = 0;
227 PDH_STATUS pdh_status;
228 BOOL refresh;
229 int ret = FAIL;
230
231 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
232
233 LOCK_PERFCOUNTERS;
234
235 if (ppsd.lastupdate_names + NAMES_UPDATE_INTERVAL >= time(NULL))
236 {
237 ret = SUCCEED;
238 goto out;
239 }
240
241 if (ppsd.lastrefresh_objects + OBJECT_CACHE_REFRESH_INTERVAL > time(NULL))
242 refresh = FALSE;
243 else
244 refresh = TRUE;
245
246 if (PDH_MORE_DATA != (pdh_status = PdhEnumObjects(NULL, NULL, NULL, &sz, PERF_DETAIL_WIZARD, refresh)))
247 {
248 zabbix_log(LOG_LEVEL_ERR, "cannot obtain required buffer size: %s",
249 strerror_from_module(pdh_status, L"PDH.DLL"));
250 goto out;
251 }
252
253 if (TRUE == refresh)
254 ppsd.lastrefresh_objects = time(NULL);
255
256 if (NULL == (p_eng = names_eng = get_all_counter_names(HKEY_PERFORMANCE_TEXT, L"Counter")) ||
257 NULL == (p_loc = names_loc = get_all_counter_names(HKEY_PERFORMANCE_NLSTEXT, L"Counter")))
258 {
259 goto out;
260 }
261
262 /* skip fields with number of records */
263 names_eng += wcslen(names_eng) + 1;
264 names_eng += wcslen(names_eng) + 1;
265 names_loc += wcslen(names_loc) + 1;
266 names_loc += wcslen(names_loc) + 1;
267
268 objects = zbx_malloc(NULL, (++sz) * sizeof(wchar_t));
269
270 if (ERROR_SUCCESS != (pdh_status = PdhEnumObjects(NULL, NULL, objects, &sz, PERF_DETAIL_WIZARD, FALSE)))
271 {
272 zabbix_log(LOG_LEVEL_ERR, "cannot obtain objects list: %s",
273 strerror_from_module(pdh_status, L"PDH.DLL"));
274 zbx_free(objects);
275 goto out;
276 }
277
278 free_object_names();
279
280 for (object = objects; L'\0' != *object; object += sz)
281 {
282 DWORD idx_eng, idx_loc;
283
284 sz = (DWORD)wcslen(object) + 1;
285 object_names = zbx_realloc(object_names, sizeof(struct object_name_ref) * (object_num + 1));
286
287 object_names[object_num].eng_name = NULL;
288 object_names[object_num].loc_name = zbx_malloc(NULL, sizeof(wchar_t) * sz);
289 memcpy(object_names[object_num].loc_name, object, sizeof(wchar_t) * sz);
290
291 /* For some objects the localized name might be missing and PdhEnumObjects() will return English */
292 /* name instead. In that case for localized name use name returned by PdhEnumObjects() if such name */
293 /* exists in English names registry (HKEY_PERFORMANCE_TEXT). */
294
295 idx_loc = 0;
296
297 for (loc_name = names_loc; L'\0' != *loc_name; loc_name += wcslen(loc_name) + 1)
298 {
299 DWORD idx;
300
301 idx = (DWORD)_wtoi(loc_name);
302 loc_name += wcslen(loc_name) + 1;
303
304 if (0 == wcscmp(object, loc_name))
305 {
306 idx_loc = idx;
307 break;
308 }
309 }
310
311 for (eng_name = names_eng; L'\0' != *eng_name; eng_name += wcslen(eng_name) + 1)
312 {
313 idx_eng = (DWORD)_wtoi(eng_name);
314 eng_name += wcslen(eng_name) + 1;
315
316 if (idx_loc == idx_eng ||
317 (0 == idx_loc && 0 == wcscmp(object_names[object_num].loc_name, eng_name)))
318 {
319 object_names[object_num].eng_name = zbx_unicode_to_utf8(eng_name);
320 break;
321 }
322 }
323
324 object_num++;
325 }
326
327 zbx_free(objects);
328 ppsd.lastupdate_names = time(NULL);
329 ret = SUCCEED;
330 out:
331 zbx_free(p_eng);
332 zbx_free(p_loc);
333
334 UNLOCK_PERFCOUNTERS;
335
336 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
337
338 return ret;
339 }
340
341 /******************************************************************************
342 * *
343 * Comments: counter is removed from the collector and *
344 * the memory is freed - do not use it again *
345 * *
346 ******************************************************************************/
remove_perf_counter(zbx_perf_counter_data_t * counter)347 void remove_perf_counter(zbx_perf_counter_data_t *counter)
348 {
349 zbx_perf_counter_data_t *cptr;
350
351 LOCK_PERFCOUNTERS;
352
353 if (NULL == counter || NULL == ppsd.pPerfCounterList)
354 goto out;
355
356 if (counter == ppsd.pPerfCounterList)
357 {
358 ppsd.pPerfCounterList = counter->next;
359 }
360 else
361 {
362 for (cptr = ppsd.pPerfCounterList; ; cptr = cptr->next)
363 {
364 if (cptr->next == counter)
365 {
366 cptr->next = counter->next;
367 break;
368 }
369 }
370 }
371
372 PdhRemoveCounter(counter->handle);
373 zbx_free(counter->name);
374 zbx_free(counter->counterpath);
375 zbx_free(counter->value_array);
376 zbx_free(counter);
377 out:
378 UNLOCK_PERFCOUNTERS;
379
380 }
381
free_perf_counter_list(void)382 static void free_perf_counter_list(void)
383 {
384 zbx_perf_counter_data_t *cptr;
385
386 while (NULL != ppsd.pPerfCounterList)
387 {
388 cptr = ppsd.pPerfCounterList;
389 ppsd.pPerfCounterList = cptr->next;
390
391 zbx_free(cptr->name);
392 zbx_free(cptr->counterpath);
393 zbx_free(cptr->value_array);
394 zbx_free(cptr);
395 }
396 }
397
398 /******************************************************************************
399 * *
400 * Comments: must be called only for PERF_COUNTER_ACTIVE counters, *
401 * interval must be less than or equal to counter->interval *
402 * *
403 ******************************************************************************/
compute_average_value(zbx_perf_counter_data_t * counter,int interval)404 static double compute_average_value(zbx_perf_counter_data_t *counter, int interval)
405 {
406 double sum = 0;
407 int i, j, count;
408
409 if (PERF_COUNTER_ACTIVE != counter->status || interval > counter->interval)
410 return 0;
411
412 if (counter->interval == interval)
413 return counter->sum / (double)counter->value_count;
414
415 /* compute the average manually for custom intervals */
416 i = counter->value_current;
417 count = (counter->value_count < interval ? counter->value_count : interval);
418
419 /* cycle backwards through the circular buffer of values */
420 for (j = 0; j < count; j++, i = (0 < i ? i - 1 : counter->interval - 1))
421 sum += counter->value_array[i];
422
423 return sum / (double)count;
424 }
425
init_perf_collector(zbx_threadedness_t threadedness,char ** error)426 int init_perf_collector(zbx_threadedness_t threadedness, char **error)
427 {
428 int ret = FAIL;
429
430 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
431
432 switch (threadedness)
433 {
434 case ZBX_SINGLE_THREADED:
435 break;
436 case ZBX_MULTI_THREADED:
437 if (SUCCEED != zbx_mutex_create(&perfstat_access, ZBX_MUTEX_PERFSTAT, error))
438 goto out;
439 break;
440 default:
441 THIS_SHOULD_NEVER_HAPPEN;
442 *error = zbx_strdup(*error, "internal error");
443 goto out;
444 }
445
446 if (ERROR_SUCCESS != zbx_PdhOpenQuery(__func__, &ppsd.pdh_query))
447 {
448 *error = zbx_strdup(*error, "cannot open performance data query");
449 goto out;
450 }
451
452 ppsd.lastrefresh_objects = 0;
453 ppsd.lastupdate_names = 0;
454
455 if (SUCCEED != init_builtin_counter_indexes())
456 {
457 *error = zbx_strdup(*error, "cannot initialize built-in counter indexes");
458 goto out;
459 }
460
461 if (SUCCEED != set_object_names())
462 {
463 *error = zbx_strdup(*error, "cannot initialize object names");
464 goto out;
465 }
466
467 ret = SUCCEED;
468 out:
469 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
470
471 return ret;
472 }
473
free_perf_collector(void)474 void free_perf_collector(void)
475 {
476 zbx_perf_counter_data_t *cptr;
477
478 if (SUCCEED != perf_collector_started())
479 return;
480
481 for (cptr = ppsd.pPerfCounterList; cptr != NULL; cptr = cptr->next)
482 {
483 if (NULL != cptr->handle)
484 {
485 PdhRemoveCounter(cptr->handle);
486 cptr->handle = NULL;
487 }
488 }
489
490 PdhCloseQuery(ppsd.pdh_query);
491 ppsd.pdh_query = NULL;
492
493 LOCK_PERFCOUNTERS;
494
495 free_perf_counter_list();
496 free_object_names();
497
498 UNLOCK_PERFCOUNTERS;
499
500 zbx_mutex_destroy(&perfstat_access);
501 }
502
collect_perfstat(void)503 void collect_perfstat(void)
504 {
505 zbx_perf_counter_data_t *cptr;
506 PDH_STATUS pdh_status;
507 time_t now;
508 PDH_FMT_COUNTERVALUE value;
509
510 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
511
512 LOCK_PERFCOUNTERS;
513
514 if (SUCCEED != perf_collector_started())
515 goto out;
516
517 if (NULL == ppsd.pPerfCounterList) /* no counters */
518 goto out;
519
520 now = time(NULL);
521
522 /* refresh unsupported counters */
523 for (cptr = ppsd.pPerfCounterList; NULL != cptr; cptr = cptr->next)
524 {
525 if (PERF_COUNTER_NOTSUPPORTED != cptr->status)
526 continue;
527
528 zbx_PdhAddCounter(__func__, cptr, ppsd.pdh_query, cptr->counterpath,
529 cptr->lang, &cptr->handle);
530 }
531
532 /* query for new data */
533 if (ERROR_SUCCESS != (pdh_status = PdhCollectQueryData(ppsd.pdh_query)))
534 {
535 for (cptr = ppsd.pPerfCounterList; NULL != cptr; cptr = cptr->next)
536 {
537 if (PERF_COUNTER_NOTSUPPORTED != cptr->status)
538 deactivate_perf_counter(cptr);
539 }
540
541 zabbix_log(LOG_LEVEL_DEBUG, "%s() call to PdhCollectQueryData() failed: %s",
542 __func__, strerror_from_module(pdh_status, L"PDH.DLL"));
543
544 goto out;
545 }
546
547 /* get the raw values */
548 for (cptr = ppsd.pPerfCounterList; NULL != cptr; cptr = cptr->next)
549 {
550 if (PERF_COUNTER_NOTSUPPORTED == cptr->status)
551 continue;
552
553 if (ERROR_SUCCESS != zbx_PdhGetRawCounterValue(__func__, cptr->counterpath,
554 cptr->handle, &cptr->rawValues[cptr->olderRawValue]))
555 {
556 deactivate_perf_counter(cptr);
557 continue;
558 }
559
560 cptr->olderRawValue = (cptr->olderRawValue + 1) & 1;
561
562 pdh_status = PdhCalculateCounterFromRawValue(cptr->handle, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100,
563 &cptr->rawValues[(cptr->olderRawValue + 1) & 1],
564 (PERF_COUNTER_INITIALIZED < cptr->status ?
565 &cptr->rawValues[cptr->olderRawValue] : NULL), &value);
566
567 if (ERROR_SUCCESS == pdh_status && PDH_CSTATUS_VALID_DATA != value.CStatus &&
568 PDH_CSTATUS_NEW_DATA != value.CStatus)
569 {
570 pdh_status = value.CStatus;
571 }
572
573 if (PDH_CSTATUS_INVALID_DATA == pdh_status)
574 {
575 /* some (e.g., rate) counters require two raw values, MSDN lacks documentation */
576 /* about what happens but tests show that PDH_CSTATUS_INVALID_DATA is returned */
577
578 cptr->status = PERF_COUNTER_GET_SECOND_VALUE;
579 continue;
580 }
581
582 /* Negative values can occur when a counter rolls over. By default, this value entry does not appear */
583 /* in the registry and Performance Monitor does not log data errors or notify the user that it has */
584 /* received bad data; More info: https://support.microsoft.com/kb/177655/EN-US */
585
586 if (PDH_CALC_NEGATIVE_DENOMINATOR == pdh_status)
587 {
588 zabbix_log(LOG_LEVEL_DEBUG, "PDH_CALC_NEGATIVE_DENOMINATOR error occurred in counterpath '%s'."
589 " Value ignored", cptr->counterpath);
590 continue;
591 }
592
593 if (PDH_CALC_NEGATIVE_VALUE == pdh_status)
594 {
595 zabbix_log(LOG_LEVEL_DEBUG, "PDH_CALC_NEGATIVE_VALUE error occurred in counterpath '%s'."
596 " Value ignored", cptr->counterpath);
597 continue;
598 }
599
600 if (ERROR_SUCCESS == pdh_status)
601 {
602 cptr->status = PERF_COUNTER_ACTIVE;
603 cptr->value_current = (cptr->value_current + 1) % cptr->interval
604
605 /* remove the oldest value, value_count will not increase */;
606 if (cptr->value_count == cptr->interval)
607 cptr->sum -= cptr->value_array[cptr->value_current];
608
609 cptr->value_array[cptr->value_current] = value.doubleValue;
610 cptr->sum += cptr->value_array[cptr->value_current];
611 if (cptr->value_count < cptr->interval)
612 cptr->value_count++;
613 }
614 else
615 {
616 zabbix_log(LOG_LEVEL_WARNING, "cannot calculate performance counter value \"%s\": %s",
617 cptr->counterpath, strerror_from_module(pdh_status, L"PDH.DLL"));
618
619 deactivate_perf_counter(cptr);
620 }
621 }
622 out:
623 UNLOCK_PERFCOUNTERS;
624
625 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
626 }
627
628 /******************************************************************************
629 * *
630 * Function: get_perf_counter_value_by_name *
631 * *
632 * Purpose: gets average named performance counter value *
633 * *
634 * Parameters: name - [IN] the performance counter name *
635 * value - [OUT] the calculated value *
636 * error - [OUT] the error message, it is not always produced *
637 * when FAIL is returned. It is a caller responsibility *
638 * to check if the error message is not NULL. *
639 * *
640 * Returns: SUCCEED - the value was retrieved successfully *
641 * FAIL - otherwise *
642 * *
643 * Comments: The value is retrieved from collector (if it has been requested *
644 * before) or directly from Windows performance counters if *
645 * possible. *
646 * *
647 ******************************************************************************/
get_perf_counter_value_by_name(const char * name,double * value,char ** error)648 int get_perf_counter_value_by_name(const char *name, double *value, char **error)
649 {
650 int ret = FAIL;
651 zbx_perf_counter_data_t *perfs = NULL;
652 char *counterpath = NULL;
653
654 zabbix_log(LOG_LEVEL_DEBUG, "In %s() name:%s", __func__, name);
655
656 LOCK_PERFCOUNTERS;
657
658 if (SUCCEED != perf_collector_started())
659 {
660 *error = zbx_strdup(*error, "Performance collector is not started.");
661 goto out;
662 }
663
664 for (perfs = ppsd.pPerfCounterList; NULL != perfs; perfs = perfs->next)
665 {
666 if (NULL != perfs->name && 0 == strcmp(perfs->name, name))
667 {
668 if (PERF_COUNTER_ACTIVE != perfs->status)
669 break;
670
671 /* the counter data is already being collected, return it */
672 *value = compute_average_value(perfs, perfs->interval);
673 ret = SUCCEED;
674 goto out;
675 }
676 }
677
678 /* we can retrieve named counter data only if it has been registered before */
679 if (NULL == perfs)
680 {
681 *error = zbx_dsprintf(*error, "Unknown performance counter name: %s.", name);
682 goto out;
683 }
684
685 counterpath = zbx_strdup(counterpath, perfs->counterpath);
686 out:
687 UNLOCK_PERFCOUNTERS;
688
689 if (NULL != counterpath)
690 {
691 /* request counter value directly from Windows performance counters */
692 PDH_STATUS pdh_status = calculate_counter_value(__func__, counterpath, perfs->lang, value);
693
694 if (PDH_NOT_IMPLEMENTED == pdh_status)
695 *error = zbx_strdup(*error, "Counter is not supported for this Microsoft Windows version");
696 else if (ERROR_SUCCESS == pdh_status)
697 ret = SUCCEED;
698
699 zbx_free(counterpath);
700 }
701
702 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
703
704 return ret;
705 }
706
707 /******************************************************************************
708 * *
709 * Function: get_perf_counter_value_by_path *
710 * *
711 * Purpose: gets average performance counter value *
712 * *
713 * Parameters: counterpath - [IN] the performance counter path *
714 * interval - [IN] the data collection interval in seconds *
715 * lang - [IN] counterpath language (default or English) *
716 * value - [OUT] the calculated value *
717 * error - [OUT] the error message *
718 * *
719 * Returns: SUCCEED - the value was retrieved successfully *
720 * FAIL - otherwise *
721 * *
722 * Comments: The value is retrieved from collector (if it has been requested *
723 * before) or directly from Windows performance counters if *
724 * possible. *
725 * *
726 ******************************************************************************/
get_perf_counter_value_by_path(const char * counterpath,int interval,zbx_perf_counter_lang_t lang,double * value,char ** error)727 int get_perf_counter_value_by_path(const char *counterpath, int interval, zbx_perf_counter_lang_t lang,
728 double *value, char **error)
729 {
730 int ret = FAIL;
731 zbx_perf_counter_data_t *perfs = NULL;
732
733 zabbix_log(LOG_LEVEL_DEBUG, "In %s() path:%s interval:%d lang:%d", __func__, counterpath,
734 interval, lang);
735
736 LOCK_PERFCOUNTERS;
737
738 if (SUCCEED != perf_collector_started())
739 {
740 *error = zbx_strdup(*error, "Performance collector is not started.");
741 goto out;
742 }
743
744 for (perfs = ppsd.pPerfCounterList; NULL != perfs; perfs = perfs->next)
745 {
746 if (0 == strcmp(perfs->counterpath, counterpath) && perfs->lang == lang)
747 {
748 if (perfs->interval < interval)
749 extend_perf_counter_interval(perfs, interval);
750
751 if (PERF_COUNTER_ACTIVE != perfs->status)
752 break;
753
754 /* the counter data is already being collected, return it */
755 *value = compute_average_value(perfs, interval);
756 ret = SUCCEED;
757 goto out;
758 }
759 }
760
761 /* if the requested counter is not already being monitored - start monitoring */
762 if (NULL == perfs)
763 perfs = add_perf_counter(NULL, counterpath, interval, lang, error);
764 out:
765 UNLOCK_PERFCOUNTERS;
766
767 if (SUCCEED != ret && NULL != perfs)
768 {
769 /* request counter value directly from Windows performance counters */
770 if (ERROR_SUCCESS == calculate_counter_value(__func__, counterpath, lang, value))
771 ret = SUCCEED;
772 }
773
774 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
775
776 return ret;
777 }
778
779 /******************************************************************************
780 * *
781 * Function: get_perf_counter_value *
782 * *
783 * Purpose: gets average value of the specified performance counter interval *
784 * *
785 * Parameters: counter - [IN] the performance counter *
786 * interval - [IN] the data collection interval in seconds *
787 * value - [OUT] the calculated value *
788 * error - [OUT] the error message *
789 * *
790 * Returns: SUCCEED - the value was retrieved successfully *
791 * FAIL - otherwise *
792 * *
793 ******************************************************************************/
get_perf_counter_value(zbx_perf_counter_data_t * counter,int interval,double * value,char ** error)794 int get_perf_counter_value(zbx_perf_counter_data_t *counter, int interval, double *value, char **error)
795 {
796 int ret = FAIL;
797
798 zabbix_log(LOG_LEVEL_DEBUG, "In %s() path:%s interval:%d", __func__, counter->counterpath, interval);
799
800 LOCK_PERFCOUNTERS;
801
802 if (SUCCEED != perf_collector_started())
803 {
804 *error = zbx_strdup(*error, "Performance collector is not started.");
805 goto out;
806 }
807
808 if (PERF_COUNTER_ACTIVE != counter->status)
809 {
810 *error = zbx_strdup(*error, "Performance counter is not ready.");
811 goto out;
812 }
813
814 *value = compute_average_value(counter, interval);
815 ret = SUCCEED;
816 out:
817 UNLOCK_PERFCOUNTERS;
818
819 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
820
821 return ret;
822 }
823
refresh_object_cache(void)824 int refresh_object_cache(void)
825 {
826 DWORD sz = 0;
827 int ret = SUCCEED;
828
829 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
830
831 LOCK_PERFCOUNTERS;
832
833 if (ppsd.lastrefresh_objects + OBJECT_CACHE_REFRESH_INTERVAL < time(NULL))
834 {
835 if (PDH_MORE_DATA != PdhEnumObjects(NULL, NULL, NULL, &sz, PERF_DETAIL_WIZARD, TRUE))
836 {
837 ret = FAIL;
838 goto out;
839 }
840
841 ppsd.lastrefresh_objects = time(NULL);
842 }
843 out:
844 UNLOCK_PERFCOUNTERS;
845
846 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
847
848 return ret;
849 }
850
get_object_name(char * eng_name)851 static wchar_t *get_object_name(char *eng_name)
852 {
853 wchar_t *loc_name = NULL;
854 int i;
855 size_t len;
856
857 LOCK_PERFCOUNTERS;
858
859 len = strlen(eng_name);
860
861 for (i = 0; i < object_num; i++)
862 {
863 if (NULL != object_names[i].eng_name && len == strlen(object_names[i].eng_name) &&
864 0 == zbx_strncasecmp(object_names[i].eng_name, eng_name, len))
865 {
866 size_t sz;
867
868 sz = (wcslen(object_names[i].loc_name) + 1) * sizeof(wchar_t);
869 loc_name = zbx_malloc(NULL, sz);
870 memcpy(loc_name, object_names[i].loc_name, sz);
871 break;
872 }
873 }
874
875 UNLOCK_PERFCOUNTERS;
876
877 return loc_name;
878 }
879
880 /******************************************************************************
881 * *
882 * Function: get_object_name_local *
883 * *
884 * Purpose: get localized name of the object *
885 * *
886 * Parameters: eng_name - [IN] english name *
887 * *
888 * Returns: localized name of the object *
889 * *
890 ******************************************************************************/
get_object_name_local(char * eng_name)891 wchar_t *get_object_name_local(char *eng_name)
892 {
893 wchar_t *name;
894
895 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
896
897 if (NULL == (name = get_object_name(eng_name)) && SUCCEED == set_object_names())
898 name = get_object_name(eng_name);
899
900 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
901
902 return name;
903 }
904