2 ** Windows Performance NetXMS subagent
3 ** Copyright (C) 2004, 2005, 2006, 2007 Victor Kirhenshtein
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.
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.
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.
30 #define MAX_CPU_COUNT 64
32 #define WPF_ENABLE_DEFAULT_COUNTERS 0x0001
39 HANDLE g_hCondShutdown
= NULL
;
46 static DWORD m_dwFlags
= WPF_ENABLE_DEFAULT_COUNTERS
;
47 static DWORD m_dwNumCPU
= 1;
48 static WINPERF_COUNTER
*m_pProcessorCounters
[MAX_CPU_COUNT
];
49 static WINPERF_COUNTER
*m_pProcessorCounters5
[MAX_CPU_COUNT
];
50 static WINPERF_COUNTER
*m_pProcessorCounters15
[MAX_CPU_COUNT
];
54 // List of predefined performance counters
59 TCHAR
*pszCounterName
;
63 TCHAR
*pszDescription
;
67 { _T("System.CPU.LoadAvg"), _T("\\System\\Processor Queue Length"), 0,
68 60, COUNTER_TYPE_FLOAT
, _T("Average CPU load for last minute"), DCI_DT_FLOAT
},
69 { _T("System.CPU.LoadAvg5"), _T("\\System\\Processor Queue Length"), 0,
70 300, COUNTER_TYPE_FLOAT
, _T("Average CPU load for last 5 minutes"), DCI_DT_FLOAT
},
71 { _T("System.CPU.LoadAvg15"), _T("\\System\\Processor Queue Length"), 0,
72 900, COUNTER_TYPE_FLOAT
, _T("Average CPU load for last 15 minutes"), DCI_DT_FLOAT
},
73 { _T("System.CPU.Usage"), _T("\\Processor(_Total)\\% Processor Time"), 0,
74 60, COUNTER_TYPE_INT32
, _T("Average CPU utilization for last minute"), DCI_DT_INT
},
75 { _T("System.CPU.Usage5"), _T("\\Processor(_Total)\\% Processor Time"), 0,
76 300, COUNTER_TYPE_INT32
, _T("Average CPU utilization for last 5 minutes"), DCI_DT_INT
},
77 { _T("System.CPU.Usage15"), _T("\\Processor(_Total)\\% Processor Time"), 0,
78 900, COUNTER_TYPE_INT32
, _T("Average CPU utilization for last 15 minutes"), DCI_DT_INT
},
79 { _T("System.IO.DiskQueue"), _T("\\PhysicalDisk(_Total)\\Avg. Disk Queue Length"), 0,
80 60, COUNTER_TYPE_FLOAT
, _T("Average disk queue length for last minute"), DCI_DT_FLOAT
},
81 { _T("System.IO.DiskTime"), _T("\\PhysicalDisk(_Total)\\% Disk Time"), 0,
82 60, COUNTER_TYPE_FLOAT
, _T("Average disk busy time for last minute"), DCI_DT_FLOAT
},
83 { NULL
, NULL
, 0, 0, 0, NULL
, 0 }
88 // Value of given performance counter
91 static LONG
H_PdhVersion(const TCHAR
*pszParam
, const TCHAR
*pArg
, TCHAR
*pValue
)
95 if (PdhGetDllVersion(&dwVersion
) != ERROR_SUCCESS
)
96 return SYSINFO_RC_ERROR
;
97 ret_uint(pValue
, dwVersion
);
98 return SYSINFO_RC_SUCCESS
;
103 // Value of CPU utilization counter
106 static LONG
H_CPUUsage(const TCHAR
*pszParam
, const TCHAR
*pArg
, TCHAR
*pValue
)
108 LONG nProcessor
, nRet
= SYSINFO_RC_SUCCESS
;
109 TCHAR
*pEnd
, szBuffer
[16];
111 if (!NxGetParameterArg(pszParam
, 1, szBuffer
, 16))
112 return SYSINFO_RC_UNSUPPORTED
;
114 nProcessor
= _tcstol(szBuffer
, &pEnd
, 0);
115 if ((*pEnd
!= 0) || (nProcessor
< 0) || (nProcessor
>= (LONG
)m_dwNumCPU
))
116 return SYSINFO_RC_UNSUPPORTED
;
120 case _T('1'): // System.CPU.Usage(*)
121 if (m_pProcessorCounters
[nProcessor
] != NULL
)
122 ret_int(pValue
, m_pProcessorCounters
[nProcessor
]->value
.iLong
);
124 nRet
= SYSINFO_RC_ERROR
;
126 case _T('2'): // System.CPU.Usage5(*)
127 if (m_pProcessorCounters5
[nProcessor
] != NULL
)
128 ret_int(pValue
, m_pProcessorCounters5
[nProcessor
]->value
.iLong
);
130 nRet
= SYSINFO_RC_ERROR
;
132 case _T('3'): // System.CPU.Usage15(*)
133 if (m_pProcessorCounters15
[nProcessor
] != NULL
)
134 ret_int(pValue
, m_pProcessorCounters15
[nProcessor
]->value
.iLong
);
136 nRet
= SYSINFO_RC_ERROR
;
139 nRet
= SYSINFO_RC_UNSUPPORTED
;
148 // Value of given counter collected by one of the collector threads
151 LONG
H_CollectedCounterData(const TCHAR
*pszParam
, const TCHAR
*pArg
, TCHAR
*pValue
)
153 switch(((WINPERF_COUNTER
*)pArg
)->wType
)
155 case COUNTER_TYPE_INT32
:
156 ret_int(pValue
, ((WINPERF_COUNTER
*)pArg
)->value
.iLong
);
158 case COUNTER_TYPE_INT64
:
159 ret_int64(pValue
, ((WINPERF_COUNTER
*)pArg
)->value
.iLarge
);
161 case COUNTER_TYPE_FLOAT
:
162 ret_double(pValue
, ((WINPERF_COUNTER
*)pArg
)->value
.dFloat
);
165 return SYSINFO_RC_SUCCESS
;
170 // Value of given performance counter
173 static LONG
H_PdhCounterValue(const TCHAR
*pszParam
, const TCHAR
*pArg
, TCHAR
*pValue
)
177 PDH_RAW_COUNTER rawData1
, rawData2
;
178 PDH_FMT_COUNTERVALUE counterValue
;
180 TCHAR szCounter
[MAX_PATH
], szBuffer
[16];
182 BOOL bUseTwoSamples
= FALSE
;
183 static TCHAR szFName
[] = _T("H_PdhCounterValue");
185 if (pArg
== NULL
) // Normal call
187 if ((!NxGetParameterArg(pszParam
, 1, szCounter
, MAX_PATH
)) ||
188 (!NxGetParameterArg(pszParam
, 2, szBuffer
, 16)))
189 return SYSINFO_RC_UNSUPPORTED
;
191 bUseTwoSamples
= _tcstol(szBuffer
, NULL
, 0) ? TRUE
: FALSE
;
193 else // Call from H_CounterAlias
195 nx_strncpy(szCounter
, pArg
, MAX_PATH
);
198 if ((rc
= PdhOpenQuery(NULL
, 0, &hQuery
)) != ERROR_SUCCESS
)
200 ReportPdhError(szFName
, _T("PdhOpenQuery"), rc
);
201 return SYSINFO_RC_ERROR
;
204 if ((rc
= PdhAddCounter(hQuery
, szCounter
, 0, &hCounter
)) != ERROR_SUCCESS
)
206 ReportPdhError(szFName
, _T("PdhAddCounter"), rc
);
207 PdhCloseQuery(hQuery
);
208 return SYSINFO_RC_UNSUPPORTED
;
212 if ((rc
= PdhCollectQueryData(hQuery
)) != ERROR_SUCCESS
)
214 ReportPdhError(szFName
, _T("PdhCollectQueryData"), rc
);
215 PdhCloseQuery(hQuery
);
216 return SYSINFO_RC_ERROR
;
218 PdhGetRawCounterValue(hCounter
, &dwType
, &rawData1
);
220 // Get second sample if required
223 Sleep(1000); // We will take second sample after one second
224 if ((rc
= PdhCollectQueryData(hQuery
)) != ERROR_SUCCESS
)
226 ReportPdhError(szFName
, _T("PdhCollectQueryData"), rc
);
227 PdhCloseQuery(hQuery
);
228 return SYSINFO_RC_ERROR
;
230 PdhGetRawCounterValue(hCounter
, NULL
, &rawData2
);
233 if (dwType
& PERF_SIZE_LARGE
)
236 PdhCalculateCounterFromRawValue(hCounter
, PDH_FMT_LARGE
,
237 &rawData2
, &rawData1
, &counterValue
);
239 PdhCalculateCounterFromRawValue(hCounter
, PDH_FMT_LARGE
,
240 &rawData1
, NULL
, &counterValue
);
241 ret_int64(pValue
, counterValue
.largeValue
);
246 PdhCalculateCounterFromRawValue(hCounter
, PDH_FMT_LONG
,
247 &rawData2
, &rawData1
, &counterValue
);
249 PdhCalculateCounterFromRawValue(hCounter
, PDH_FMT_LONG
,
250 &rawData1
, NULL
, &counterValue
);
251 ret_int(pValue
, counterValue
.longValue
);
253 PdhCloseQuery(hQuery
);
254 return SYSINFO_RC_SUCCESS
;
259 // List of available performance objects
262 static LONG
H_PdhObjects(const TCHAR
*pszParam
, const TCHAR
*pArg
, NETXMS_VALUES_LIST
*pValue
)
264 TCHAR
*pszObject
, *pszObjList
, szHostName
[256];
265 LONG iResult
= SYSINFO_RC_ERROR
;
270 if (GetComputerName(szHostName
, &dwSize
))
273 pszObjList
= (TCHAR
*)malloc(sizeof(TCHAR
) * dwSize
);
274 if ((rc
= PdhEnumObjects(NULL
, szHostName
, pszObjList
, &dwSize
,
275 PERF_DETAIL_WIZARD
, TRUE
)) == ERROR_SUCCESS
)
277 for(pszObject
= pszObjList
; *pszObject
!= 0; pszObject
+= _tcslen(pszObject
) + 1)
278 NxAddResultString(pValue
, pszObject
);
279 iResult
= SYSINFO_RC_SUCCESS
;
283 ReportPdhError(_T("H_PdhObjects"), _T("PdhEnumObjects"), rc
);
292 // List of available performance items for given object
295 static LONG
H_PdhObjectItems(const TCHAR
*pszParam
, const TCHAR
*pArg
, NETXMS_VALUES_LIST
*pValue
)
297 TCHAR
*pszElement
, *pszCounterList
, *pszInstanceList
, szHostName
[256], szObject
[256];
298 LONG iResult
= SYSINFO_RC_ERROR
;
299 DWORD dwSize1
, dwSize2
;
302 NxGetParameterArg(pszParam
, 1, szObject
, 256);
303 if (szObject
[0] != 0)
306 if (GetComputerName(szHostName
, &dwSize1
))
308 dwSize1
= dwSize2
= 256000;
309 pszCounterList
= (TCHAR
*)malloc(sizeof(TCHAR
) * dwSize1
);
310 pszInstanceList
= (TCHAR
*)malloc(sizeof(TCHAR
) * dwSize2
);
311 rc
= PdhEnumObjectItems(NULL
, szHostName
, szObject
,
312 pszCounterList
, &dwSize1
, pszInstanceList
, &dwSize2
,
313 PERF_DETAIL_WIZARD
, 0);
314 if ((rc
== ERROR_SUCCESS
) || (rc
== PDH_MORE_DATA
))
316 for(pszElement
= (pArg
[0] == _T('C')) ? pszCounterList
: pszInstanceList
;
317 *pszElement
!= 0; pszElement
+= _tcslen(pszElement
) + 1)
318 NxAddResultString(pValue
, pszElement
);
319 iResult
= SYSINFO_RC_SUCCESS
;
323 ReportPdhError(_T("H_PdhObjectItems"), _T("PdhEnumObjectItems"), rc
);
325 free(pszCounterList
);
326 free(pszInstanceList
);
331 iResult
= SYSINFO_RC_UNSUPPORTED
;
338 // Value of specific performance parameter, which is mapped one-to-one to
339 // performance counter. Actually, it's an alias for PDH.CounterValue(xxx) parameter.
342 static LONG
H_CounterAlias(const TCHAR
*pszParam
, const TCHAR
*pArg
, TCHAR
*pValue
)
344 return H_PdhCounterValue(NULL
, pArg
, pValue
);
349 // Initialize subagent
352 static BOOL
SubAgentInit(Config
*config
)
354 // Create shutdown condition object
355 g_hCondShutdown
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
356 StartCollectorThreads();
362 // Handler for subagent unload
365 static void SubAgentShutdown(void)
367 if (g_hCondShutdown
!= NULL
)
368 SetEvent(g_hCondShutdown
);
374 // Subagent information
377 static NETXMS_SUBAGENT_PARAM m_parameters
[] =
379 { _T("PDH.CounterValue(*)"), H_PdhCounterValue
, NULL
, DCI_DT_INT
, _T("") },
380 { _T("PDH.Version"), H_PdhVersion
, NULL
, DCI_DT_UINT
, _T("Version of PDH.DLL") },
381 { _T("System.CPU.Usage(*)"), H_CPUUsage
, "1", DCI_DT_INT
, "Average CPU {instance} utilization for last minute" },
382 { _T("System.CPU.Usage5(*)"), H_CPUUsage
, "2", DCI_DT_INT
, "Average CPU {instance} utilization for last 5 minutes" },
383 { _T("System.CPU.Usage15(*)"), H_CPUUsage
, "3", DCI_DT_INT
, "Average CPU {instance} utilization for last 15 minutes" },
384 { _T("System.ThreadCount"), H_CounterAlias
, _T("\\System\\Threads"), DCI_DT_UINT
, _T("Total number of threads") },
385 { _T("System.Uptime"), H_CounterAlias
, _T("\\System\\System Up Time"), DCI_DT_UINT
, _T("System uptime") }
387 static NETXMS_SUBAGENT_ENUM m_enums
[] =
389 { _T("PDH.ObjectCounters(*)"), H_PdhObjectItems
, _T("C") },
390 { _T("PDH.ObjectInstances(*)"), H_PdhObjectItems
, _T("I") },
391 { _T("PDH.Objects"), H_PdhObjects
, NULL
}
394 static NETXMS_SUBAGENT_INFO m_info
=
396 NETXMS_SUBAGENT_INFO_MAGIC
,
397 _T("WinPerf"), _T(NETXMS_VERSION_STRING
) _T(DEBUG_SUFFIX
),
398 SubAgentInit
, SubAgentShutdown
, NULL
, // handlers
399 0, NULL
, // parameters
400 sizeof(m_enums
) / sizeof(NETXMS_SUBAGENT_ENUM
),
407 // Add new parameter to list
410 BOOL
AddParameter(TCHAR
*pszName
, LONG (* fpHandler
)(const TCHAR
*, const TCHAR
*, TCHAR
*),
411 TCHAR
*pArg
, int iDataType
, TCHAR
*pszDescription
)
415 for(i
= 0; i
< m_info
.dwNumParameters
; i
++)
416 if (!_tcsicmp(pszName
, m_info
.pParamList
[i
].szName
))
419 if (i
== m_info
.dwNumParameters
)
422 m_info
.dwNumParameters
++;
424 (NETXMS_SUBAGENT_PARAM
*)realloc(m_info
.pParamList
,
425 sizeof(NETXMS_SUBAGENT_PARAM
) * m_info
.dwNumParameters
);
428 nx_strncpy(m_info
.pParamList
[i
].szName
, pszName
, MAX_PARAM_NAME
);
429 m_info
.pParamList
[i
].fpHandler
= fpHandler
;
430 m_info
.pParamList
[i
].pArg
= pArg
;
431 m_info
.pParamList
[i
].iDataType
= iDataType
;
432 nx_strncpy(m_info
.pParamList
[i
].szDescription
, pszDescription
, MAX_DB_STRING
);
439 // Add predefined counters
442 static void AddPredefinedCounters(void)
445 WINPERF_COUNTER
*pCnt
;
447 TCHAR szBuffer
[MAX_PATH
];
449 for(i
= 0; m_counterList
[i
].pszParamName
!= NULL
; i
++)
451 pCnt
= AddCounter(m_counterList
[i
].pszCounterName
, m_counterList
[i
].iClass
,
452 m_counterList
[i
].iNumSamples
, m_counterList
[i
].iDataType
);
454 AddParameter(m_counterList
[i
].pszParamName
, H_CollectedCounterData
,
455 (TCHAR
*)pCnt
, m_counterList
[i
].iDCIDataType
,
456 m_counterList
[i
].pszDescription
);
459 // Add CPU utilization counters
460 GetSystemInfo(&sysInfo
);
461 m_dwNumCPU
= sysInfo
.dwNumberOfProcessors
;
462 for(i
= 0; i
< m_dwNumCPU
; i
++)
464 _sntprintf(szBuffer
, MAX_PATH
, _T("\\Processor(%d)\\%% Processor Time"), i
);
465 m_pProcessorCounters
[i
] = AddCounter(szBuffer
, 0, 60, COUNTER_TYPE_INT32
);
466 m_pProcessorCounters5
[i
] = AddCounter(szBuffer
, 0, 300, COUNTER_TYPE_INT32
);
467 m_pProcessorCounters15
[i
] = AddCounter(szBuffer
, 0, 900, COUNTER_TYPE_INT32
);
473 // Configuration file template
476 static TCHAR
*m_pszCounterList
= NULL
;
477 static NX_CFG_TEMPLATE m_cfgTemplate
[] =
479 { _T("Counter"), CT_STRING_LIST
, _T('\n'), 0, 0, 0, &m_pszCounterList
},
480 { _T("EnableDefaultCounters"), CT_BOOLEAN
, 0, 0, WPF_ENABLE_DEFAULT_COUNTERS
, 0, &m_dwFlags
},
481 { _T(""), CT_END_OF_LIST
, 0, 0, 0, 0, NULL
}
486 // Entry point for NetXMS agent
489 extern "C" BOOL
__declspec(dllexport
) __cdecl
490 NxSubAgentRegister(NETXMS_SUBAGENT_INFO
**ppInfo
, Config
*config
)
492 DWORD i
, dwBufferSize
, dwBytes
, dwType
, dwStatus
;
493 TCHAR
*pBuffer
, *newName
;
495 if (m_info
.pParamList
!= NULL
)
496 return FALSE
; // Most likely another instance of WINPERF subagent already loaded
498 // Read performance counter indexes
503 dwBufferSize
+= 8192;
504 pBuffer
= (TCHAR
*)realloc(pBuffer
, dwBufferSize
);
505 dwBytes
= dwBufferSize
;
506 dwStatus
= RegQueryValueEx(HKEY_PERFORMANCE_DATA
, _T("Counter 009"), NULL
, &dwType
, (BYTE
*)pBuffer
, &dwBytes
);
507 } while(dwStatus
== ERROR_MORE_DATA
);
508 if (dwStatus
== ERROR_SUCCESS
)
510 CreateCounterIndex(pBuffer
);
514 // Init parameters list
515 m_info
.dwNumParameters
= sizeof(m_parameters
) / sizeof(NETXMS_SUBAGENT_PARAM
);
516 m_info
.pParamList
= (NETXMS_SUBAGENT_PARAM
*)nx_memdup(m_parameters
, sizeof(m_parameters
));
518 // Check counter names for H_CounterAlias
519 for(i
= 0; i
< m_info
.dwNumParameters
; i
++)
521 if (m_info
.pParamList
[i
].fpHandler
== H_CounterAlias
)
523 CheckCounter(m_info
.pParamList
[i
].pArg
, &newName
);
525 m_info
.pParamList
[i
].pArg
= newName
;
529 // Load configuration
530 bool success
= config
->bindParameters(_T("WinPerf"), m_cfgTemplate
);
535 // Parse counter list
536 if (m_pszCounterList
!= NULL
)
538 for(pItem
= m_pszCounterList
; *pItem
!= 0; pItem
= pEnd
+ 1)
540 pEnd
= _tcschr(pItem
, _T('\n'));
544 if (!AddCounterFromConfig(pItem
))
545 NxWriteAgentLog(EVENTLOG_WARNING_TYPE
,
546 _T("Unable to add counter from configuration file. ")
547 _T("Original configuration record: %s"), pItem
);
549 free(m_pszCounterList
);
552 if (m_dwFlags
& WPF_ENABLE_DEFAULT_COUNTERS
)
553 AddPredefinedCounters();
557 safe_free(m_pszCounterList
);
568 BOOL WINAPI
DllMain(HINSTANCE hInstance
, DWORD dwReason
, LPVOID lpReserved
)
570 if (dwReason
== DLL_PROCESS_ATTACH
)
571 DisableThreadLibraryCalls(hInstance
);