Status PerfCounterCollector::collect(BSONObjBuilder* builder) { // Ask PDH to collect the counters PDH_STATUS status = PdhCollectQueryData(_query); if (status != ERROR_SUCCESS) { return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhCollectQueryData", status)}; } // Output timebase // Counters that are based on time either use 100NS or System Ticks Per Second. // We only need to output system ticks per second once if any counter depends on it. // This is typically 3320310. if (_timeBaseTicksCounter) { int64_t timebase; status = PdhGetCounterTimeBase(_timeBaseTicksCounter->handle, &timebase); if (status != ERROR_SUCCESS) { return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhGetCounterTimeBase", status)}; } builder->append("timebase", timebase); } // Retrieve all the values that PDH collected for us. for (const auto& counterGroup : _counters) { BSONObjBuilder subObjBuilder(builder->subobjStart(counterGroup.name)); Status s = collectCounters(counterGroup.counters, &subObjBuilder); if (!s.isOK()) { return s; } subObjBuilder.doneFast(); } for (const auto& counterGroup : _nestedCounters) { BSONObjBuilder subObjBuilder(builder->subobjStart(counterGroup.name)); for (const auto& instanceNamePair : counterGroup.counters) { BSONObjBuilder instSubObjBuilder(builder->subobjStart(instanceNamePair.first)); Status s = collectCounters(instanceNamePair.second, &instSubObjBuilder); if (!s.isOK()) { return s; } instSubObjBuilder.doneFast(); } subObjBuilder.doneFast(); } return Status::OK(); }
StatusWith<std::vector<PerfCounterCollector::CounterInfo>> PerfCounterCollector::addCounters( StringData path) { std::wstring pathWide = toNativeString(path.toString().c_str()); DWORD pathListLength = 0; PDH_STATUS status = PdhExpandCounterPathW(pathWide.c_str(), nullptr, &pathListLength); if (status != PDH_MORE_DATA) { return {ErrorCodes::WindowsPdhError, str::stream() << formatFunctionCallError("PdhExpandCounterPathW", status) << " for counter '" << path << "'"}; } auto buf = stdx::make_unique<wchar_t[]>(pathListLength); status = PdhExpandCounterPathW(pathWide.c_str(), buf.get(), &pathListLength); if (status != ERROR_SUCCESS) { return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhExpandCounterPathW", status)}; } std::vector<CounterInfo> counters; // Windows' PdhExpandWildCardPathW returns a nullptr terminated array of nullptr separated // strings. std::vector<std::string> counterNames; const wchar_t* ptr = buf.get(); while (ptr && *ptr) { counterNames.emplace_back(toUtf8String(ptr)); ptr += wcslen(ptr) + 1; } // Sort to ensure we have a predictable ordering in the final BSON std::sort(counterNames.begin(), counterNames.end()); for (const auto& name : counterNames) { auto swCounterInfo = addCounter(name); if (!swCounterInfo.isOK()) { return swCounterInfo.getStatus(); } counters.emplace_back(std::move(swCounterInfo.getValue())); } return {std::move(counters)}; }
Status PerfCounterCollector::collectCounters(const std::vector<CounterInfo>& counters, BSONObjBuilder* builder) { for (const auto& counterInfo : counters) { DWORD dwType = 0; PDH_RAW_COUNTER rawCounter = {0}; PDH_STATUS status = PdhGetRawCounterValue(counterInfo.handle, &dwType, &rawCounter); if (status != ERROR_SUCCESS) { return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhGetRawCounterValue", status)}; } if (counterInfo.hasSecondValue) { // Delta, Rate, and Elapsed Time counters require the second value in the raw counter // information builder->append(counterInfo.firstName, rawCounter.FirstValue); builder->append(counterInfo.secondName, rawCounter.SecondValue); } else { builder->append(counterInfo.firstName, rawCounter.FirstValue); } } return Status::OK(); }
Status PerfCounterCollector::open() { PDH_STATUS status = PdhOpenQueryW(nullptr, NULL, &_query); if (status != ERROR_SUCCESS) { return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhOpenQueryW", status)}; } return Status::OK(); }
Status PerfCounterCollector::collectCounters(const std::vector<CounterInfo>& counters, BSONObjBuilder* builder) { for (const auto& counterInfo : counters) { DWORD dwType = 0; // Elapsed Time is an unusual counter in that being able to control the sample period for // the counter is uninteresting even though it is computed from two values. Just return the // computed value instead. if (counterInfo.type == PERF_ELAPSED_TIME) { PDH_FMT_COUNTERVALUE counterValue = {0}; PDH_STATUS status = PdhGetFormattedCounterValue( counterInfo.handle, PDH_FMT_DOUBLE, &dwType, &counterValue); if (status != ERROR_SUCCESS) { return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhGetFormattedCounterValue", status)}; } builder->append(counterInfo.firstName, counterValue.doubleValue); } else { PDH_RAW_COUNTER rawCounter = {0}; PDH_STATUS status = PdhGetRawCounterValue(counterInfo.handle, &dwType, &rawCounter); if (status != ERROR_SUCCESS) { return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhGetRawCounterValue", status)}; } if (counterInfo.hasSecondValue) { // Precise counters require the second value in the raw counter information builder->append(counterInfo.firstName, rawCounter.FirstValue); builder->append(counterInfo.secondName, rawCounter.SecondValue); } else { builder->append(counterInfo.firstName, rawCounter.FirstValue); } } } return Status::OK(); }
StatusWith<PerfCounterCollector::CounterInfo> PerfCounterCollector::addCounter(StringData path) { PDH_HCOUNTER counter{0}; PDH_STATUS status = PdhAddCounterW(_query, toNativeString(path.toString().c_str()).c_str(), NULL, &counter); if (status != ERROR_SUCCESS) { return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhAddCounterW", status)}; } DWORD bufferSize = 0; status = PdhGetCounterInfoW(counter, false, &bufferSize, nullptr); if (status != PDH_MORE_DATA) { return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhGetCounterInfoW", status)}; } auto buf = stdx::make_unique<char[]>(bufferSize); auto counterInfo = reinterpret_cast<PPDH_COUNTER_INFO>(buf.get()); status = PdhGetCounterInfoW(counter, false, &bufferSize, counterInfo); if (status != ERROR_SUCCESS) { return {ErrorCodes::WindowsPdhError, formatFunctionCallError("PdhGetCounterInfoW", status)}; } // A full qualified path is as such: // "\\MYMACHINE\\Processor(0)\\% Idle Time" // MachineName \\ Object Name (Instance Name) \\ CounterName // Ex: // MachineName: MYMACHINE // Object Name: Processor // InstanceName: 0 // CounterName: % Idle Time // We do not want to use Machine Name, but sometimes we want InstanceName // std::string firstName = str::stream() << '\\' << toUtf8String(counterInfo->szObjectName) << '\\' << toUtf8String(counterInfo->szCounterName); // Compute a second name std::string secondName(firstName); bool hasSecondValue = false; // Only include base for counters that need it if ((counterInfo->dwType & PERF_COUNTER_PRECISION) == PERF_COUNTER_PRECISION) { secondName += " Base"; hasSecondValue = true; } // InstanceName is null for counters without instance names return {CounterInfo{std::move(firstName), std::move(secondName), hasSecondValue, counterInfo->szInstanceName ? toUtf8String(counterInfo->szInstanceName) : std::string(), counterInfo->dwType, counter}}; }