summaryrefslogtreecommitdiffstats
path: root/plugins/check_perfmon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_perfmon.cpp')
-rw-r--r--plugins/check_perfmon.cpp387
1 files changed, 387 insertions, 0 deletions
diff --git a/plugins/check_perfmon.cpp b/plugins/check_perfmon.cpp
new file mode 100644
index 0000000..0f94b12
--- /dev/null
+++ b/plugins/check_perfmon.cpp
@@ -0,0 +1,387 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "plugins/thresholds.hpp"
+#include <boost/program_options.hpp>
+#include <iostream>
+#include <vector>
+#include <windows.h>
+#include <pdh.h>
+#include <pdhmsg.h>
+#include <shlwapi.h>
+
+#define VERSION 1.0
+
+namespace po = boost::program_options;
+
+struct printInfoStruct
+{
+ threshold tWarn;
+ threshold tCrit;
+ std::wstring wsFullPath;
+ double dValue;
+ DWORD dwPerformanceWait = 1000;
+ DWORD dwRequestedType = PDH_FMT_DOUBLE;
+};
+
+static bool parseArguments(const int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo)
+{
+ WCHAR szNamePath[MAX_PATH + 1];
+ GetModuleFileName(NULL, szNamePath, MAX_PATH);
+ WCHAR *szProgName = PathFindFileName(szNamePath);
+
+ po::options_description desc("Options");
+ desc.add_options()
+ ("help,h", "Print help page and exit")
+ ("version,V", "Print version and exit")
+ ("warning,w", po::wvalue<std::wstring>(), "Warning thershold")
+ ("critical,c", po::wvalue<std::wstring>(), "Critical threshold")
+ ("performance-counter,P", po::wvalue<std::wstring>(), "The performance counter string to use")
+ ("performance-wait", po::value<DWORD>(), "Sleep in milliseconds between the two perfomance querries (Default: 1000ms)")
+ ("fmt-countertype", po::wvalue<std::wstring>(), "Value type of counter: 'double'(default), 'long', 'int64'")
+ ("print-objects", "Prints all available objects to console")
+ ("print-object-info", "Prints all available instances and counters of --performance-counter, do not use a full perfomance counter string here")
+ ("perf-syntax", po::wvalue<std::wstring>(), "Use this string as name for the performance counter (graphite compatibility)")
+ ;
+
+ po::wcommand_line_parser parser(ac, av);
+
+ try {
+ po::store(
+ parser
+ .options(desc)
+ .style(
+ po::command_line_style::unix_style |
+ po::command_line_style::allow_long_disguise)
+ .run(),
+ vm);
+ vm.notify();
+ } catch (const std::exception& e) {
+ std::cout << e.what() << '\n' << desc << '\n';
+ return false;
+ }
+
+ if (vm.count("version")) {
+ std::wcout << "Version: " << VERSION << '\n';
+ return false;
+ }
+
+ if (vm.count("help")) {
+ std::wcout << szProgName << " Help\n\tVersion: " << VERSION << '\n';
+ wprintf(
+ L"%s runs a check against a performance counter.\n"
+ L"You can use the following options to define its behaviour:\n\n", szProgName);
+ std::cout << desc;
+ wprintf(
+ L"\nIt will then output a string looking something like this:\n\n"
+ L"\tPERFMON CRITICAL \"\\Processor(_Total)\\%% Idle Time\" = 40.34 | "
+ L"perfmon=40.34;20;40;; \"\\Processor(_Total)\\%% Idle Time\"=40.34\n\n"
+ L"\"tPERFMON\" being the type of the check, \"CRITICAL\" the returned status\n"
+ L"and \"40.34\" is the performance counters value.\n"
+ L"%s' exit codes denote the following:\n"
+ L" 0\tOK,\n\tNo Thresholds were exceeded\n"
+ L" 1\tWARNING,\n\tThe warning was broken, but not the critical threshold\n"
+ L" 2\tCRITICAL,\n\tThe critical threshold was broken\n"
+ L" 3\tUNKNOWN, \n\tNo check could be performed\n\n"
+ , szProgName);
+ return false;
+ }
+
+ if (vm.count("warning")) {
+ try {
+ printInfo.tWarn = threshold(vm["warning"].as<std::wstring>());
+ } catch (const std::invalid_argument& e) {
+ std::wcout << e.what() << '\n';
+ return false;
+ }
+ }
+
+ if (vm.count("critical")) {
+ try {
+ printInfo.tCrit = threshold(vm["critical"].as<std::wstring>());
+ } catch (const std::invalid_argument& e) {
+ std::wcout << e.what() << '\n';
+ return false;
+ }
+ }
+
+ if (vm.count("fmt-countertype")) {
+ if (!vm["fmt-countertype"].as<std::wstring>().compare(L"int64"))
+ printInfo.dwRequestedType = PDH_FMT_LARGE;
+ else if (!vm["fmt-countertype"].as<std::wstring>().compare(L"long"))
+ printInfo.dwRequestedType = PDH_FMT_LONG;
+ else if (vm["fmt-countertype"].as<std::wstring>().compare(L"double")) {
+ std::wcout << "Unknown value type " << vm["fmt-countertype"].as<std::wstring>() << '\n';
+ return false;
+ }
+ }
+
+ if (vm.count("performance-counter"))
+ printInfo.wsFullPath = vm["performance-counter"].as<std::wstring>();
+
+ if (vm.count("performance-wait"))
+ printInfo.dwPerformanceWait = vm["performance-wait"].as<DWORD>();
+
+ return true;
+}
+
+static bool getInstancesAndCountersOfObject(const std::wstring& wsObject,
+ std::vector<std::wstring>& vecInstances, std::vector<std::wstring>& vecCounters)
+{
+ DWORD dwCounterListLength = 0, dwInstanceListLength = 0;
+
+ if (PdhEnumObjectItems(NULL, NULL, wsObject.c_str(),
+ NULL, &dwCounterListLength, NULL,
+ &dwInstanceListLength, PERF_DETAIL_WIZARD, 0) != PDH_MORE_DATA)
+ return false;
+
+ std::vector<WCHAR> mszCounterList(dwCounterListLength + 1);
+ std::vector<WCHAR> mszInstanceList(dwInstanceListLength + 1);
+
+ if (FAILED(PdhEnumObjectItems(NULL, NULL, wsObject.c_str(),
+ mszCounterList.data(), &dwCounterListLength, mszInstanceList.data(),
+ &dwInstanceListLength, PERF_DETAIL_WIZARD, 0))) {
+ return false;
+ }
+
+ if (dwInstanceListLength) {
+ std::wstringstream wssInstanceName;
+
+ // XXX: is the "- 1" correct?
+ for (DWORD c = 0; c < dwInstanceListLength - 1; ++c) {
+ if (mszInstanceList[c])
+ wssInstanceName << mszInstanceList[c];
+ else {
+ vecInstances.push_back(wssInstanceName.str());
+ wssInstanceName.str(L"");
+ }
+ }
+ }
+
+ if (dwCounterListLength) {
+ std::wstringstream wssCounterName;
+
+ // XXX: is the "- 1" correct?
+ for (DWORD c = 0; c < dwCounterListLength - 1; ++c) {
+ if (mszCounterList[c])
+ wssCounterName << mszCounterList[c];
+ else {
+ vecCounters.push_back(wssCounterName.str());
+ wssCounterName.str(L"");
+ }
+ }
+ }
+
+ return true;
+}
+
+static void printPDHError(PDH_STATUS status)
+{
+ HMODULE hPdhLibrary = NULL;
+ LPWSTR pMessage = NULL;
+
+ hPdhLibrary = LoadLibrary(L"pdh.dll");
+ if (!hPdhLibrary) {
+ std::wcout << "LoadLibrary failed with " << GetLastError() << '\n';
+ return;
+ }
+
+ if (!FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ hPdhLibrary, status, 0, (LPWSTR)&pMessage, 0, NULL)) {
+ FreeLibrary(hPdhLibrary);
+ std::wcout << "Format message failed with " << std::hex << GetLastError() << '\n';
+ return;
+ }
+
+ FreeLibrary(hPdhLibrary);
+
+ std::wcout << pMessage << '\n';
+ LocalFree(pMessage);
+}
+
+static void printObjects()
+{
+ DWORD dwBufferLength = 0;
+ PDH_STATUS status = PdhEnumObjects(NULL, NULL, NULL,
+ &dwBufferLength, PERF_DETAIL_WIZARD, FALSE);
+ //HEX HEX! Only a Magicians gets all the info he wants, and only Microsoft knows what that means
+
+ if (status != PDH_MORE_DATA) {
+ printPDHError(status);
+ return;
+ }
+
+ std::vector<WCHAR> mszObjectList(dwBufferLength + 2);
+ status = PdhEnumObjects(NULL, NULL, mszObjectList.data(),
+ &dwBufferLength, PERF_DETAIL_WIZARD, FALSE);
+
+ if (FAILED(status)) {
+ printPDHError(status);
+ return;
+ }
+
+ DWORD c = 0;
+
+ while (++c < dwBufferLength) {
+ if (mszObjectList[c] == '\0')
+ std::wcout << '\n';
+ else
+ std::wcout << mszObjectList[c];
+ }
+}
+
+static void printObjectInfo(const printInfoStruct& pI)
+{
+ if (pI.wsFullPath.empty()) {
+ std::wcout << "No object given!\n";
+ return;
+ }
+
+ std::vector<std::wstring> vecInstances, vecCounters;
+
+ if (!getInstancesAndCountersOfObject(pI.wsFullPath, vecInstances, vecCounters)) {
+ std::wcout << "Could not enumerate instances and counters of " << pI.wsFullPath << '\n'
+ << "Make sure it exists!\n";
+ return;
+ }
+
+ std::wcout << "Instances of " << pI.wsFullPath << ":\n";
+ if (vecInstances.empty())
+ std::wcout << "> Has no instances\n";
+ else {
+ for (const auto& instance : vecInstances)
+ std::wcout << "> " << instance << '\n';
+ }
+ std::wcout << std::endl;
+
+ std::wcout << "Performance Counters of " << pI.wsFullPath << ":\n";
+ if (vecCounters.empty())
+ std::wcout << "> Has no counters\n";
+ else {
+ for (const auto& counter : vecCounters)
+ std::wcout << "> " << counter << "\n";
+ }
+ std::wcout << std::endl;
+}
+
+bool QueryPerfData(printInfoStruct& pI)
+{
+ PDH_HQUERY hQuery = NULL;
+ PDH_HCOUNTER hCounter = NULL;
+ DWORD dwBufferSize = 0, dwItemCount = 0;
+
+ if (pI.wsFullPath.empty()) {
+ std::wcout << "No performance counter path given!\n";
+ return false;
+ }
+
+ PDH_FMT_COUNTERVALUE_ITEM *pDisplayValues = NULL;
+
+ PDH_STATUS status = PdhOpenQuery(NULL, NULL, &hQuery);
+ if (FAILED(status))
+ goto die;
+
+ status = PdhAddEnglishCounter(hQuery, pI.wsFullPath.c_str(), NULL, &hCounter);
+
+ if (FAILED(status))
+ status = PdhAddCounter(hQuery, pI.wsFullPath.c_str(), NULL, &hCounter);
+
+ if (FAILED(status))
+ goto die;
+
+ status = PdhCollectQueryData(hQuery);
+ if (FAILED(status))
+ goto die;
+
+ /*
+ * Most counters need two queries to provide a value.
+ * Those which need only one will return the second.
+ */
+ Sleep(pI.dwPerformanceWait);
+
+ status = PdhCollectQueryData(hQuery);
+ if (FAILED(status))
+ goto die;
+
+ status = PdhGetFormattedCounterArray(hCounter, pI.dwRequestedType, &dwBufferSize, &dwItemCount, NULL);
+ if (status != PDH_MORE_DATA)
+ goto die;
+
+ pDisplayValues = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(new BYTE[dwBufferSize]);
+ status = PdhGetFormattedCounterArray(hCounter, pI.dwRequestedType, &dwBufferSize, &dwItemCount, pDisplayValues);
+
+ if (FAILED(status))
+ goto die;
+
+ switch (pI.dwRequestedType) {
+ case (PDH_FMT_LONG):
+ pI.dValue = pDisplayValues[0].FmtValue.longValue;
+ break;
+ case (PDH_FMT_LARGE):
+ pI.dValue = (double) pDisplayValues[0].FmtValue.largeValue;
+ break;
+ default:
+ pI.dValue = pDisplayValues[0].FmtValue.doubleValue;
+ break;
+ }
+
+ delete[]pDisplayValues;
+
+ return true;
+
+die:
+ printPDHError(status);
+ delete[]pDisplayValues;
+ return false;
+}
+
+static int printOutput(const po::variables_map& vm, printInfoStruct& pi)
+{
+ std::wstringstream wssPerfData;
+
+ if (vm.count("perf-syntax"))
+ wssPerfData << "'" << vm["perf-syntax"].as<std::wstring>() << "'=";
+ else
+ wssPerfData << "'" << pi.wsFullPath << "'=";
+
+ wssPerfData << pi.dValue << ';' << pi.tWarn.pString() << ';' << pi.tCrit.pString() << ";;";
+
+ if (pi.tCrit.rend(pi.dValue)) {
+ std::wcout << "PERFMON CRITICAL for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
+ << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n";
+ return 2;
+ }
+
+ if (pi.tWarn.rend(pi.dValue)) {
+ std::wcout << "PERFMON WARNING for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
+ << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n";
+ return 1;
+ }
+
+ std::wcout << "PERFMON OK for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as<std::wstring>() : pi.wsFullPath)
+ << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n";
+
+ return 0;
+}
+
+int wmain(int argc, WCHAR **argv)
+{
+ po::variables_map variables_map;
+ printInfoStruct stPrintInfo;
+ if (!parseArguments(argc, argv, variables_map, stPrintInfo))
+ return 3;
+
+ if (variables_map.count("print-objects")) {
+ printObjects();
+ return 0;
+ }
+
+ if (variables_map.count("print-object-info")) {
+ printObjectInfo(stPrintInfo);
+ return 0;
+ }
+
+ if (QueryPerfData(stPrintInfo))
+ return printOutput(variables_map, stPrintInfo);
+ else
+ return 3;
+}