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 "perfmon.h"
23 #include "log.h"
24
25 static ZBX_THREAD_LOCAL zbx_perf_counter_id_t *PerfCounterList = NULL;
26
27 /* This struct contains mapping between built-in English object names and PDH indexes. */
28 /* If you change it then you also need to add enum values to zbx_builtin_object_ref_t. */
29 static struct builtin_object_ref
30 {
31 unsigned long pdhIndex;
32 wchar_t eng_name[PDH_MAX_COUNTER_NAME];
33 DWORD minSupported_dwMajorVersion;
34 DWORD minSupported_dwMinorVersion;
35 }
36 builtin_object_map[] =
37 {
38 { 0, L"System", 0, 0 },
39 { 0, L"Processor", 0, 0 },
40 { 0, L"Processor Information", 6, 1 },
41 { 0, L"Terminal Services", 0, 0 }
42 };
43
44 /* this enum must be only modified along with builtin_object_map[] */
45 typedef enum
46 {
47 POI_SYSTEM = 0,
48 POI_PROCESSOR,
49 POI_PROCESSOR_INFORMATION,
50 POI_TERMINAL_SERVICES,
51 POI_MAX_INDEX = POI_TERMINAL_SERVICES
52 }
53 zbx_builtin_object_ref_t;
54
55 /* This struct contains mapping between built-in English counter names and PDH indexes. */
56 /* If you change it then you also need to add enum values to zbx_builtin_counter_ref_t. */
57 static struct builtin_counter_ref
58 {
59 unsigned long pdhIndex;
60 zbx_builtin_object_ref_t object;
61 wchar_t eng_name[PDH_MAX_COUNTER_NAME];
62 DWORD minSupported_dwMajorVersion;
63 DWORD minSupported_dwMinorVersion;
64 }
65 builtin_counter_map[] =
66 {
67 { 0, POI_SYSTEM, L"Processor Queue Length", 0, 0},
68 { 0, POI_SYSTEM, L"System Up Time", 0, 0},
69 { 0, POI_PROCESSOR, L"% Processor Time", 0, 0},
70 { 0, POI_PROCESSOR_INFORMATION, L"% Processor Time", 6, 1},
71 { 0, POI_TERMINAL_SERVICES, L"Total Sessions", 0, 0}
72 };
73
zbx_PdhMakeCounterPath(const char * function,PDH_COUNTER_PATH_ELEMENTS * cpe,char * counterpath)74 PDH_STATUS zbx_PdhMakeCounterPath(const char *function, PDH_COUNTER_PATH_ELEMENTS *cpe, char *counterpath)
75 {
76 DWORD dwSize = PDH_MAX_COUNTER_PATH;
77 wchar_t *wcounterPath = NULL;
78 PDH_STATUS pdh_status;
79
80 wcounterPath = zbx_malloc(wcounterPath, sizeof(wchar_t) * PDH_MAX_COUNTER_PATH);
81
82 if (ERROR_SUCCESS != (pdh_status = PdhMakeCounterPath(cpe, wcounterPath, &dwSize, 0)))
83 {
84 char *object, *counter;
85
86 object = zbx_unicode_to_utf8(cpe->szObjectName);
87 counter = zbx_unicode_to_utf8(cpe->szCounterName);
88
89 zabbix_log(LOG_LEVEL_ERR, "%s(): cannot make counterpath for \"\\%s\\%s\": %s",
90 function, object, counter, strerror_from_module(pdh_status, L"PDH.DLL"));
91
92 zbx_free(counter);
93 zbx_free(object);
94 }
95 else
96 zbx_unicode_to_utf8_static(wcounterPath, counterpath, PDH_MAX_COUNTER_PATH);
97
98 zbx_free(wcounterPath);
99
100 return pdh_status;
101 }
102
zbx_PdhOpenQuery(const char * function,PDH_HQUERY query)103 PDH_STATUS zbx_PdhOpenQuery(const char *function, PDH_HQUERY query)
104 {
105 PDH_STATUS pdh_status;
106
107 if (ERROR_SUCCESS != (pdh_status = PdhOpenQuery(NULL, 0, query)))
108 {
109 zabbix_log(LOG_LEVEL_ERR, "%s(): call to PdhOpenQuery() failed: %s",
110 function, strerror_from_module(pdh_status, L"PDH.DLL"));
111 }
112
113 return pdh_status;
114 }
115
116 /******************************************************************************
117 * *
118 * Comments: counter is NULL if it is not in the collector, *
119 * do not call it for PERF_COUNTER_ACTIVE counters *
120 * *
121 ******************************************************************************/
zbx_PdhAddCounter(const char * function,zbx_perf_counter_data_t * counter,PDH_HQUERY query,const char * counterpath,zbx_perf_counter_lang_t lang,PDH_HCOUNTER * handle)122 PDH_STATUS zbx_PdhAddCounter(const char *function, zbx_perf_counter_data_t *counter, PDH_HQUERY query,
123 const char *counterpath, zbx_perf_counter_lang_t lang, PDH_HCOUNTER *handle)
124 {
125 /* pointer type to PdhAddEnglishCounterW() */
126 typedef PDH_STATUS (WINAPI *ADD_ENG_COUNTER)(PDH_HQUERY, LPCWSTR, DWORD_PTR, PDH_HCOUNTER);
127
128 PDH_STATUS pdh_status = ERROR_SUCCESS;
129 wchar_t *wcounterPath = NULL;
130 int need_english;
131
132 ZBX_THREAD_LOCAL static ADD_ENG_COUNTER add_eng_counter;
133 ZBX_THREAD_LOCAL static int first_call = 1;
134
135 need_english = PERF_COUNTER_LANG_DEFAULT != lang ||
136 (NULL != counter && PERF_COUNTER_LANG_DEFAULT != counter->lang);
137
138 /* PdhAddEnglishCounterW() is only available on Windows 2008/Vista and onwards, */
139 /* so we need to resolve it dynamically and fail if it's not available */
140 if (0 != first_call && 0 != need_english)
141 {
142 if (NULL == (add_eng_counter = (ADD_ENG_COUNTER)GetProcAddress(GetModuleHandle(L"PDH.DLL"),
143 "PdhAddEnglishCounterW")))
144 {
145 zabbix_log(LOG_LEVEL_WARNING, "PdhAddEnglishCounter() is not available, "
146 "perf_counter_en[] is not supported");
147 }
148
149 first_call = 0;
150 }
151
152 if (0 != need_english && NULL == add_eng_counter)
153 {
154 pdh_status = PDH_NOT_IMPLEMENTED;
155 }
156
157 if (ERROR_SUCCESS == pdh_status)
158 {
159 wcounterPath = zbx_utf8_to_unicode(counterpath);
160 }
161
162 if (ERROR_SUCCESS == pdh_status && NULL == *handle)
163 {
164 pdh_status = need_english ?
165 add_eng_counter(query, wcounterPath, 0, handle) :
166 PdhAddCounter(query, wcounterPath, 0, handle);
167 }
168
169 if (ERROR_SUCCESS != pdh_status && NULL != *handle)
170 {
171 if (ERROR_SUCCESS == PdhRemoveCounter(*handle))
172 *handle = NULL;
173 }
174
175 if (ERROR_SUCCESS == pdh_status)
176 {
177 if (NULL != counter)
178 counter->status = PERF_COUNTER_INITIALIZED;
179
180 zabbix_log(LOG_LEVEL_DEBUG, "%s(): PerfCounter '%s' successfully added", function, counterpath);
181 }
182 else
183 {
184 if (NULL != counter)
185 counter->status = PERF_COUNTER_NOTSUPPORTED;
186
187 zabbix_log(LOG_LEVEL_DEBUG, "%s(): unable to add PerfCounter '%s': %s",
188 function, counterpath, strerror_from_module(pdh_status, L"PDH.DLL"));
189 }
190
191 zbx_free(wcounterPath);
192
193 return pdh_status;
194 }
195
zbx_PdhCollectQueryData(const char * function,const char * counterpath,PDH_HQUERY query)196 PDH_STATUS zbx_PdhCollectQueryData(const char *function, const char *counterpath, PDH_HQUERY query)
197 {
198 PDH_STATUS pdh_status;
199
200 if (ERROR_SUCCESS != (pdh_status = PdhCollectQueryData(query)))
201 {
202 zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot collect data '%s': %s",
203 function, counterpath, strerror_from_module(pdh_status, L"PDH.DLL"));
204 }
205
206 return pdh_status;
207 }
208
zbx_PdhGetRawCounterValue(const char * function,const char * counterpath,PDH_HCOUNTER handle,PPDH_RAW_COUNTER value)209 PDH_STATUS zbx_PdhGetRawCounterValue(const char *function, const char *counterpath, PDH_HCOUNTER handle, PPDH_RAW_COUNTER value)
210 {
211 PDH_STATUS pdh_status;
212
213 if (ERROR_SUCCESS != (pdh_status = PdhGetRawCounterValue(handle, NULL, value)) ||
214 (PDH_CSTATUS_VALID_DATA != value->CStatus && PDH_CSTATUS_NEW_DATA != value->CStatus))
215 {
216 if (ERROR_SUCCESS == pdh_status)
217 pdh_status = value->CStatus;
218
219 zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot get counter value '%s': %s",
220 function, counterpath, strerror_from_module(pdh_status, L"PDH.DLL"));
221 }
222
223 return pdh_status;
224 }
225
226 /******************************************************************************
227 * *
228 * Comments: Get the value of a counter. If it is a rate counter, *
229 * sleep 1 second to get the second raw value. *
230 * *
231 ******************************************************************************/
calculate_counter_value(const char * function,const char * counterpath,zbx_perf_counter_lang_t lang,double * value)232 PDH_STATUS calculate_counter_value(const char *function, const char *counterpath,
233 zbx_perf_counter_lang_t lang, double *value)
234 {
235 PDH_HQUERY query;
236 PDH_HCOUNTER handle = NULL;
237 PDH_STATUS pdh_status;
238 PDH_RAW_COUNTER rawData, rawData2;
239 PDH_FMT_COUNTERVALUE counterValue;
240
241 if (ERROR_SUCCESS != (pdh_status = zbx_PdhOpenQuery(function, &query)))
242 return pdh_status;
243
244 if (ERROR_SUCCESS != (pdh_status = zbx_PdhAddCounter(function, NULL, query, counterpath, lang, &handle)))
245 goto close_query;
246
247 if (ERROR_SUCCESS != (pdh_status = zbx_PdhCollectQueryData(function, counterpath, query)))
248 goto remove_counter;
249
250 if (ERROR_SUCCESS != (pdh_status = zbx_PdhGetRawCounterValue(function, counterpath, handle, &rawData)))
251 goto remove_counter;
252
253 if (PDH_CSTATUS_INVALID_DATA == (pdh_status = PdhCalculateCounterFromRawValue(handle, PDH_FMT_DOUBLE |
254 PDH_FMT_NOCAP100, &rawData, NULL, &counterValue)))
255 {
256 /* some (e.g., rate) counters require two raw values, MSDN lacks documentation */
257 /* about what happens but tests show that PDH_CSTATUS_INVALID_DATA is returned */
258
259 zbx_sleep(1);
260
261 if (ERROR_SUCCESS == (pdh_status = zbx_PdhCollectQueryData(function, counterpath, query)) &&
262 ERROR_SUCCESS == (pdh_status = zbx_PdhGetRawCounterValue(function, counterpath,
263 handle, &rawData2)))
264 {
265 pdh_status = PdhCalculateCounterFromRawValue(handle, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100,
266 &rawData2, &rawData, &counterValue);
267 }
268 }
269
270 if (ERROR_SUCCESS != pdh_status || (PDH_CSTATUS_VALID_DATA != counterValue.CStatus &&
271 PDH_CSTATUS_NEW_DATA != counterValue.CStatus))
272 {
273 if (ERROR_SUCCESS == pdh_status)
274 pdh_status = counterValue.CStatus;
275
276 zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot calculate counter value '%s': %s",
277 function, counterpath, strerror_from_module(pdh_status, L"PDH.DLL"));
278 }
279 else
280 {
281 *value = counterValue.doubleValue;
282 }
283 remove_counter:
284 PdhRemoveCounter(handle);
285 close_query:
286 PdhCloseQuery(query);
287
288 return pdh_status;
289 }
290
291 /******************************************************************************
292 * *
293 * Function: get_builtin_object_index *
294 * *
295 * Purpose: get performance object index by reference value described by *
296 * zbx_builtin_counter_ref_t enum *
297 * *
298 * Parameters: counter_ref - [IN] built-in performance object *
299 * *
300 * Comments: Performance object index values can differ across Windows *
301 * installations for the same names *
302 * *
303 ******************************************************************************/
get_builtin_object_index(zbx_builtin_counter_ref_t counter_ref)304 DWORD get_builtin_object_index(zbx_builtin_counter_ref_t counter_ref)
305 {
306 return builtin_object_map[builtin_counter_map[counter_ref].object].pdhIndex;
307 }
308
309 /******************************************************************************
310 * *
311 * Function: get_builtin_counter_index *
312 * *
313 * Purpose: get performance counter index by reference value described by *
314 * zbx_builtin_counter_ref_t enum *
315 * *
316 * Parameters: counter_ref - [IN] built-in performance counter *
317 * *
318 * Comments: Performance counter index values can differ across Windows *
319 * installations for the same names *
320 * *
321 ******************************************************************************/
get_builtin_counter_index(zbx_builtin_counter_ref_t counter_ref)322 DWORD get_builtin_counter_index(zbx_builtin_counter_ref_t counter_ref)
323 {
324 return builtin_counter_map[counter_ref].pdhIndex;
325 }
326
327 /******************************************************************************
328 * *
329 * Function: get_all_counter_names *
330 * *
331 * Purpose: function to read counter names/help from registry *
332 * *
333 * Parameters: reg_key - [IN] registry key *
334 * reg_value_name - [IN] name of the registry value *
335 * *
336 * Return value: wchar_t* buffer with list of strings on success, *
337 * NULL on failure *
338 * *
339 * Comments: This function should be normally called with reg_key parameter *
340 * set to HKEY_PERFORMANCE_NLSTEXT (localized names) or *
341 * HKEY_PERFORMANCE_TEXT (English names); and reg_value_name *
342 * parameter set to L"Counter" parameter. It returns a list of *
343 * null-terminated string pairs. Last string is followed by *
344 * an additional null-terminator. The return buffer must be freed *
345 * by the caller. *
346 * *
347 ******************************************************************************/
get_all_counter_names(HKEY reg_key,wchar_t * reg_value_name)348 wchar_t *get_all_counter_names(HKEY reg_key, wchar_t *reg_value_name)
349 {
350 wchar_t *buffer = NULL;
351 DWORD buffer_size = 0;
352 LSTATUS status = ERROR_SUCCESS;
353
354 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
355
356 /* query the size of the text data for further buffer allocation */
357 if (ERROR_SUCCESS != (status = RegQueryValueEx(reg_key, reg_value_name, NULL, NULL, NULL, &buffer_size)))
358 {
359 zabbix_log(LOG_LEVEL_ERR, "RegQueryValueEx() failed at getting buffer size, 0x%lx",
360 (unsigned long)status);
361 goto finish;
362 }
363
364 buffer = (wchar_t*)zbx_malloc(buffer, (size_t)buffer_size);
365
366 if (ERROR_SUCCESS != (status = RegQueryValueEx(reg_key, reg_value_name, NULL, NULL, (LPBYTE)buffer,
367 &buffer_size)))
368 {
369 zabbix_log(LOG_LEVEL_ERR, "RegQueryValueEx() failed with 0x%lx", (unsigned long)status);
370 zbx_free(buffer);
371 goto finish;
372 }
373 finish:
374 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
375
376 return buffer;
377 }
378
379 /******************************************************************************
380 * *
381 * Function: get_perf_name_by_index *
382 * *
383 * Purpose: fills performance counter name based on its index *
384 * *
385 * Parameters: index - [IN] PDH counter index *
386 * name - [OUT] counter name buffer *
387 * size - [IN] counter name buffer size *
388 * *
389 * Return value: SUCCEED if counter data is valid, *
390 * FAIL otherwise *
391 * *
392 ******************************************************************************/
get_perf_name_by_index(DWORD index,wchar_t * name,DWORD size)393 static int get_perf_name_by_index(DWORD index, wchar_t *name, DWORD size)
394 {
395 int ret = SUCCEED;
396 PDH_STATUS pdh_status;
397
398 if (ERROR_SUCCESS != (pdh_status = PdhLookupPerfNameByIndex(NULL, index, name, &size)))
399 {
400 zabbix_log(LOG_LEVEL_ERR, "PdhLookupPerfNameByIndex() failed: %s",
401 strerror_from_module(pdh_status, L"PDH.DLL"));
402 ret = FAIL;
403 }
404
405 return ret;
406 }
407
408 /******************************************************************************
409 * *
410 * Function: validate_counter_path *
411 * *
412 * Purpose: checks if a specified counter path data is pointing to a valid *
413 * counter *
414 * *
415 * Parameters: cpe - [IN] PDH counter path data *
416 * *
417 * Return value: SUCCEED if counter data is valid, *
418 * FAIL otherwise *
419 * *
420 ******************************************************************************/
validate_counter_path(PDH_COUNTER_PATH_ELEMENTS * cpe)421 static int validate_counter_path(PDH_COUNTER_PATH_ELEMENTS *cpe)
422 {
423 int ret = FAIL;
424 DWORD s = 0;
425 PDH_STATUS pdh_status;
426 wchar_t *path = NULL;
427
428 if (PDH_MORE_DATA == (pdh_status = PdhMakeCounterPath(cpe, NULL, &s, 0)))
429 {
430 path = zbx_malloc(path, sizeof(wchar_t) * s);
431
432 if (ERROR_SUCCESS != (pdh_status = PdhMakeCounterPath(cpe, path, &s, 0)))
433 {
434 zabbix_log(LOG_LEVEL_WARNING, "PdhMakeCounterPath() failed: %s",
435 strerror_from_module(pdh_status, L"PDH.DLL"));
436 }
437 else if (ERROR_SUCCESS != (pdh_status = PdhValidatePath(path)))
438 {
439 if (PDH_CSTATUS_NO_COUNTER != pdh_status && PDH_CSTATUS_NO_INSTANCE != pdh_status)
440 {
441 zabbix_log(LOG_LEVEL_DEBUG, "PdhValidatePath() szObjectName:%s szCounterName:%s"
442 " failed: %s", cpe->szObjectName, cpe->szCounterName,
443 strerror_from_module(pdh_status, L"PDH.DLL"));
444 }
445 }
446 else
447 {
448 ret = SUCCEED;
449 }
450
451 zbx_free(path);
452 }
453 else
454 {
455 zabbix_log(LOG_LEVEL_DEBUG, "PdhMakeCounterPath() failed: %s",
456 strerror_from_module(pdh_status, L"PDH.DLL"));
457 }
458
459 return ret;
460 }
461
462 /******************************************************************************
463 * *
464 * Function: validate_object_counter *
465 * *
466 * Purpose: checks if specified counter is valid successor of the object *
467 * *
468 * Parameters: object - [IN] PDH object index *
469 * counter - [IN] PDH counter index *
470 * *
471 * Return value: SUCCEED if object - counter combination is valid, *
472 * FAIL otherwise *
473 * *
474 ******************************************************************************/
validate_object_counter(DWORD object,DWORD counter)475 static int validate_object_counter(DWORD object, DWORD counter)
476 {
477 PDH_COUNTER_PATH_ELEMENTS *cpe;
478 int ret = SUCCEED;
479
480 cpe = (PDH_COUNTER_PATH_ELEMENTS *)zbx_malloc(NULL, sizeof(PDH_COUNTER_PATH_ELEMENTS));
481 memset(cpe, 0, sizeof(PDH_COUNTER_PATH_ELEMENTS));
482
483 cpe->szObjectName = zbx_malloc(NULL, sizeof(wchar_t) * PDH_MAX_COUNTER_NAME);
484 cpe->szCounterName = zbx_malloc(NULL, sizeof(wchar_t) * PDH_MAX_COUNTER_NAME);
485
486 if (SUCCEED != get_perf_name_by_index(object, cpe->szObjectName, PDH_MAX_COUNTER_NAME) ||
487 SUCCEED != get_perf_name_by_index(counter, cpe->szCounterName, PDH_MAX_COUNTER_NAME))
488 {
489 ret = FAIL;
490 goto out;
491 }
492
493 if (SUCCEED != validate_counter_path(cpe))
494 {
495 /* try with "any" instance name */
496 cpe->szInstanceName = L"*";
497
498 if (SUCCEED != validate_counter_path(cpe))
499 ret = FAIL;
500 }
501
502 out:
503 zbx_free(cpe->szCounterName);
504 zbx_free(cpe->szObjectName);
505 zbx_free(cpe);
506
507 return ret;
508 }
509
510 /******************************************************************************
511 * *
512 * Function: init_builtin_counter_indexes *
513 * *
514 * Purpose: Scans registry key with all performance counter English names *
515 * and obtains system-dependent PDH counter indexes for further *
516 * use by corresponding items. *
517 * *
518 * Return value: SUCCEED/FAIL *
519 * *
520 * Comments: This function should be normally called during agent *
521 * initialization from init_perf_collector(). *
522 * *
523 ******************************************************************************/
init_builtin_counter_indexes(void)524 int init_builtin_counter_indexes(void)
525 {
526 int ret = SUCCEED, i;
527 wchar_t *counter_text, *eng_names, *counter_base;
528 DWORD counter_index;
529 static const OSVERSIONINFOEX *vi = NULL;
530
531 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
532
533 if (NULL == vi && NULL == (vi = zbx_win_getversion()))
534 {
535 zabbix_log(LOG_LEVEL_ERR, "Failed to get windows version");
536 ret = FAIL;
537 goto out;
538 }
539
540 /* Get buffer holding a list of performance counter indexes and English counter names. */
541 /* L"Counter" stores names, L"Help" stores descriptions ("Help" is not used). */
542 if (NULL == (counter_base = eng_names = get_all_counter_names(HKEY_PERFORMANCE_TEXT, L"Counter")))
543 {
544 ret = FAIL;
545 goto out;
546 }
547
548 /* bypass first pair of counter data elements - these contain number of records */
549 counter_base += wcslen(counter_base) + 1;
550 counter_base += wcslen(counter_base) + 1;
551
552 /* get builtin object names */
553 for (counter_text = counter_base; 0 != *counter_text; counter_text += wcslen(counter_text) + 1)
554 {
555 counter_index = (DWORD)_wtoi(counter_text);
556 counter_text += wcslen(counter_text) + 1;
557
558 for (i = 0; i < ARRSIZE(builtin_object_map); i++)
559 {
560 if (0 == builtin_object_map[i].pdhIndex && vi->dwMajorVersion >=
561 builtin_object_map[i].minSupported_dwMajorVersion && vi->dwMinorVersion >=
562 builtin_object_map[i].minSupported_dwMinorVersion && 0 ==
563 wcscmp(builtin_object_map[i].eng_name, counter_text))
564 {
565 builtin_object_map[i].pdhIndex = counter_index;
566 break;
567 }
568 }
569 }
570
571 /* Get builtin counter names. There may be counter name duplicates. */
572 /* Validate them in combination with parent object. */
573 for (counter_text = counter_base; 0 != *counter_text; counter_text += wcslen(counter_text) + 1)
574 {
575 counter_index = (DWORD)_wtoi(counter_text);
576 counter_text += wcslen(counter_text) + 1;
577
578 for (i = 0; i < ARRSIZE(builtin_counter_map); i++)
579 {
580 if (0 == builtin_counter_map[i].pdhIndex && vi->dwMajorVersion >=
581 builtin_counter_map[i].minSupported_dwMajorVersion && vi->dwMinorVersion >=
582 builtin_counter_map[i].minSupported_dwMinorVersion && 0 ==
583 wcscmp(builtin_counter_map[i].eng_name, counter_text) && SUCCEED ==
584 validate_object_counter(get_builtin_object_index(i), counter_index))
585 {
586 builtin_counter_map[i].pdhIndex = counter_index;
587 break;
588 }
589 }
590 }
591
592 zbx_free(eng_names);
593
594 #define CHECK_COUNTER_INDICES(index_map) \
595 for (i = 0; i < ARRSIZE(index_map); i++) \
596 { \
597 if (0 == index_map[i].pdhIndex && vi->dwMajorVersion >= \
598 index_map[i].minSupported_dwMajorVersion && vi->dwMinorVersion >= \
599 builtin_counter_map[i].minSupported_dwMinorVersion) \
600 { \
601 char *counter; \
602 \
603 counter = zbx_unicode_to_utf8(index_map[i].eng_name); \
604 zabbix_log(LOG_LEVEL_ERR, "Failed to initialize builtin counter: %s", counter); \
605 zbx_free(counter); \
606 } \
607 }
608
609 /* check if all builtin counter indices are filled */
610 CHECK_COUNTER_INDICES(builtin_object_map);
611 CHECK_COUNTER_INDICES(builtin_counter_map);
612
613 #undef CHECK_COUNTER_INDICES
614 out:
615 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
616
617 return ret;
618 }
619
620 /******************************************************************************
621 * *
622 * Function: get_counter_name *
623 * *
624 * Purpose: get performance object or counter name by PDH index *
625 * *
626 * Parameters: pdhIndex - [IN] built-in performance counter index *
627 * *
628 * Return value: PDH performance counter name *
629 * or "UnknownPerformanceCounter" on failure *
630 * *
631 * Comments: Performance counter index values can differ across Windows *
632 * installations for the same names *
633 * *
634 ******************************************************************************/
get_counter_name(DWORD pdhIndex)635 wchar_t *get_counter_name(DWORD pdhIndex)
636 {
637 zbx_perf_counter_id_t *counterName;
638
639 zabbix_log(LOG_LEVEL_DEBUG, "In %s() pdhIndex:%u", __func__, pdhIndex);
640
641 counterName = PerfCounterList;
642 while (NULL != counterName)
643 {
644 if (counterName->pdhIndex == pdhIndex)
645 break;
646 counterName = counterName->next;
647 }
648
649 if (NULL == counterName)
650 {
651 counterName = (zbx_perf_counter_id_t *)zbx_malloc(counterName, sizeof(zbx_perf_counter_id_t));
652
653 memset(counterName, 0, sizeof(zbx_perf_counter_id_t));
654 counterName->pdhIndex = pdhIndex;
655 counterName->next = PerfCounterList;
656
657 if (SUCCEED == get_perf_name_by_index(pdhIndex, counterName->name, PDH_MAX_COUNTER_NAME))
658 {
659 PerfCounterList = counterName;
660 }
661 else
662 {
663 zbx_free(counterName);
664 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():FAIL", __func__);
665 return L"UnknownPerformanceCounter";
666 }
667 }
668
669 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():SUCCEED", __func__);
670
671 return counterName->name;
672 }
673
check_counter_path(char * counterPath,int convert_from_numeric)674 int check_counter_path(char *counterPath, int convert_from_numeric)
675 {
676 PDH_COUNTER_PATH_ELEMENTS *cpe = NULL;
677 PDH_STATUS status;
678 int ret = FAIL;
679 DWORD dwSize = 0;
680 wchar_t *wcounterPath;
681
682 wcounterPath = zbx_utf8_to_unicode(counterPath);
683
684 status = PdhParseCounterPath(wcounterPath, NULL, &dwSize, 0);
685 if (PDH_MORE_DATA == status || ERROR_SUCCESS == status)
686 {
687 cpe = (PDH_COUNTER_PATH_ELEMENTS *)zbx_malloc(cpe, dwSize);
688 }
689 else
690 {
691 zabbix_log(LOG_LEVEL_ERR, "cannot get required buffer size for counter path '%s': %s",
692 counterPath, strerror_from_module(status, L"PDH.DLL"));
693 goto clean;
694 }
695
696 if (ERROR_SUCCESS != (status = PdhParseCounterPath(wcounterPath, cpe, &dwSize, 0)))
697 {
698 zabbix_log(LOG_LEVEL_ERR, "cannot parse counter path '%s': %s",
699 counterPath, strerror_from_module(status, L"PDH.DLL"));
700 goto clean;
701 }
702
703 if (0 != convert_from_numeric)
704 {
705 int is_numeric = (SUCCEED == _wis_uint(cpe->szObjectName) ? 0x01 : 0);
706 is_numeric |= (SUCCEED == _wis_uint(cpe->szCounterName) ? 0x02 : 0);
707
708 if (0 != is_numeric)
709 {
710 if (0x01 & is_numeric)
711 cpe->szObjectName = get_counter_name(_wtoi(cpe->szObjectName));
712 if (0x02 & is_numeric)
713 cpe->szCounterName = get_counter_name(_wtoi(cpe->szCounterName));
714
715 if (ERROR_SUCCESS != zbx_PdhMakeCounterPath(__func__, cpe, counterPath))
716 goto clean;
717
718 zabbix_log(LOG_LEVEL_DEBUG, "counter path converted to '%s'", counterPath);
719 }
720 }
721
722 ret = SUCCEED;
723 clean:
724 zbx_free(cpe);
725 zbx_free(wcounterPath);
726
727 return ret;
728 }
729