blob: c547039b97a2c05f2b1419b012f59914918249dd [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/chrome_watcher/system_load_estimator.h"
#include <vector>
#include "base/logging.h"
#include "base/macros.h"
#include "base/threading/platform_thread.h"
namespace chrome_watcher {
namespace {
using CounterHandle = SystemLoadEstimator::CounterHandle;
uint64_t FileTimeToUInt64(FILETIME time) {
ULARGE_INTEGER output = {};
output.LowPart = time.dwLowDateTime;
output.HighPart = time.dwHighDateTime;
return static_cast<uint64_t>(output.QuadPart);
}
CounterHandle AddPdhCounter(PDH_HQUERY query,
const wchar_t* object_name,
const wchar_t* counter_name) {
DCHECK(counter_name);
// Get the counter's path.
PDH_COUNTER_PATH_ELEMENTS path_elements = {
nullptr, const_cast<LPTSTR>(object_name), nullptr, nullptr,
0, const_cast<LPTSTR>(counter_name)};
std::unique_ptr<wchar_t[]> counter_path(new wchar_t[PDH_MAX_COUNTER_PATH]);
DWORD path_len = PDH_MAX_COUNTER_PATH;
PDH_STATUS status =
::PdhMakeCounterPath(&path_elements, counter_path.get(), &path_len, 0);
if (status != ERROR_SUCCESS)
return CounterHandle();
// Add the counter.
HCOUNTER counter_raw = nullptr;
status = ::PdhAddCounter(query, counter_path.get(), 0, &counter_raw);
if (status != ERROR_SUCCESS)
return CounterHandle();
return CounterHandle(counter_raw);
}
bool GetDoubleCounterValue(HCOUNTER counter, int* value) {
DCHECK(value);
PDH_FMT_COUNTERVALUE counter_value = {};
PDH_STATUS status = ::PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE,
nullptr, &counter_value);
if (status != ERROR_SUCCESS)
return false;
*value = static_cast<int>(counter_value.doubleValue);
return true;
}
} // namespace
void QueryDeleter::operator()(PDH_HQUERY query) const {
if (query)
::PdhCloseQuery(query);
}
void CounterDeleter::operator()(PDH_HCOUNTER counter) const {
if (counter)
::PdhRemoveCounter(counter);
}
bool SystemLoadEstimator::Measure(Estimate* estimate) {
DCHECK(estimate);
SystemLoadEstimator estimator;
if (!estimator.Initialize())
return false;
return estimator.PerformMeasurements(estimate);
}
SystemLoadEstimator::SystemLoadEstimator() = default;
SystemLoadEstimator::~SystemLoadEstimator() = default;
bool SystemLoadEstimator::Initialize() {
// Open the PDH query.
PDH_HQUERY query_raw;
if (::PdhOpenQuery(nullptr, 0, &query_raw) != ERROR_SUCCESS)
return false;
pdh_query_.reset(query_raw);
// Add counters to the query.
pdh_disk_idle_counter_ =
AddPdhCounter(pdh_query_.get(), L"LogicalDisk(C:)", L"% Idle Time");
if (!pdh_disk_idle_counter_)
return false;
pdh_disk_queue_counter_ = AddPdhCounter(pdh_query_.get(), L"LogicalDisk(C:)",
L"Avg. Disk Queue Length");
if (!pdh_disk_queue_counter_)
return false;
return true;
}
bool SystemLoadEstimator::PerformMeasurements(Estimate* estimate) {
return PerformMeasurements(base::TimeDelta::FromSeconds(1), estimate);
}
bool SystemLoadEstimator::PerformMeasurements(base::TimeDelta duration,
Estimate* estimate) {
DCHECK(estimate);
DCHECK(pdh_query_.get());
DCHECK(pdh_disk_idle_counter_.get());
DCHECK(pdh_disk_queue_counter_.get());
// Initial cpu measurement.
uint64_t cpu_total_start = 0ULL;
uint64_t cpu_idle_start = 0ULL;
if (!GetTotalAndIdleTimes(&cpu_total_start, &cpu_idle_start))
return false;
// Initial disk measurement.
if (::PdhCollectQueryData(pdh_query_.get()) != ERROR_SUCCESS)
return false;
// Wait a bit.
base::PlatformThread::Sleep(duration);
// Final cpu measurement.
uint64_t cpu_total_end = 0ULL;
uint64_t cpu_idle_end = 0ULL;
if (!GetTotalAndIdleTimes(&cpu_total_end, &cpu_idle_end))
return false;
uint64_t cpu_total = 0ULL;
cpu_total = cpu_total_end - cpu_total_start;
uint64_t cpu_idle = 0ULL;
cpu_idle = cpu_idle_end - cpu_idle_start;
if (cpu_total == 0)
return false; // The measurement is deemed invalid.
estimate->cpu_load_pct = static_cast<int>(
100. * static_cast<double>(cpu_total - cpu_idle) / cpu_total);
// Final disk measurement.
if (::PdhCollectQueryData(pdh_query_.get()) != ERROR_SUCCESS)
return false;
if (!GetDoubleCounterValue(pdh_disk_idle_counter_.get(),
&estimate->disk_idle_pct)) {
return false;
}
return GetDoubleCounterValue(pdh_disk_queue_counter_.get(),
&estimate->avg_disk_queue_len);
}
bool SystemLoadEstimator::GetTotalAndIdleTimes(uint64_t* total,
uint64_t* idle) {
DCHECK(total);
DCHECK(idle);
FILETIME time_idle = {};
FILETIME time_kernel = {};
FILETIME time_user = {};
if (!::GetSystemTimes(&time_idle, &time_kernel, &time_user))
return false;
// Note: kernel time includes idle time.
*idle = FileTimeToUInt64(time_idle);
*total = FileTimeToUInt64(time_kernel) + FileTimeToUInt64(time_user);
return true;
}
} // namespace chrome_watcher