| Index: base/test/energy_monitor_mac.cc
|
| diff --git a/base/test/energy_monitor_mac.cc b/base/test/energy_monitor_mac.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f25bca74f10cb50a5b66e853b5e75cd82c3906eb
|
| --- /dev/null
|
| +++ b/base/test/energy_monitor_mac.cc
|
| @@ -0,0 +1,154 @@
|
| +// Copyright 2015 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 "base/test/energy_monitor_mac.h"
|
| +
|
| +#include <IntelPowerGadget/EnergyLib.h>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/bind_helpers.h"
|
| +#include "base/files/file_util.h"
|
| +#include "base/logging.h"
|
| +#include "base/run_loop.h"
|
| +
|
| +namespace base {
|
| +namespace test {
|
| +namespace {
|
| +
|
| +enum {
|
| + POWER_DATA_AVERAGE_WATTS = 0,
|
| + POWER_DATA_CUMULATIVE_JOULES = 1,
|
| + POWER_DATA_CUMULATIVE_MILLIWATT_HOURS = 2
|
| +};
|
| +
|
| +const int kDefaultDelayMs = 50;
|
| +
|
| +void CheckResult(int result) {
|
| + CHECK_NE(0, result) << "Intel API call failed.";
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +EnergyMonitorMac::EnergyMonitorMac()
|
| + : sample_delay_(base::TimeDelta::FromMilliseconds(kDefaultDelayMs)) {
|
| + static int initialize_once = IntelEnergyLibInitialize();
|
| + CHECK_NE(0, initialize_once) << "Failed to initialize.";
|
| +
|
| + int num_nodes = 0;
|
| + CheckResult(GetNumNodes(&num_nodes));
|
| + CHECK_EQ(1, num_nodes) << "Only supports 1 node for now.";
|
| +
|
| + int num_msrs = 0;
|
| + CheckResult(GetNumMsrs(&num_msrs));
|
| +
|
| + for (int msr = 0; msr < num_msrs; ++msr) {
|
| + int msr_func = 0;
|
| + CheckResult(GetMsrFunc(msr, &msr_func));
|
| + if (msr_func != MSR_FUNC_POWER)
|
| + continue; // Ignore frequency and temperature MSRs.
|
| +
|
| + char msr_name[4096]; // The API doesn't document a max length for this.
|
| + CheckResult(GetMsrName(msr, msr_name));
|
| +
|
| + if (strcmp(msr_name, "IA") == 0) {
|
| + DCHECK_EQ(-1, samples_[IA].msr_index) << "Multiple IA MSRs";
|
| + samples_[IA].msr_index = msr;
|
| + }
|
| +
|
| + if (strcmp(msr_name, "Processor") == 0) {
|
| + DCHECK_EQ(-1, samples_[PROCESSOR].msr_index) << "Multiple Processor MSRs";
|
| + samples_[PROCESSOR].msr_index = msr;
|
| + }
|
| + }
|
| +}
|
| +
|
| +EnergyMonitorMac::~EnergyMonitorMac() {}
|
| +
|
| +void EnergyMonitorMac::Run(const base::TimeDelta& warmup_delay,
|
| + size_t num_samples) {
|
| + DCHECK_LT(1u, num_samples) << "Need at least 2 samples.";
|
| + target_sample_count_ = num_samples;
|
| +
|
| + // First step is to read a sample, but record nothing. This allows
|
| + // measurements such as watts to establish the first time interval.
|
| + MessageLoop::current()->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&EnergyMonitorMac::ReadNextSample, base::Unretained(this)),
|
| + warmup_delay);
|
| +
|
| + base::RunLoop run_loop;
|
| + quit_closure_ = run_loop.QuitClosure();
|
| + run_loop.Run();
|
| +}
|
| +
|
| +void EnergyMonitorMac::SetSampleDelay(const base::TimeDelta& sample_delay) {}
|
| +
|
| +double EnergyMonitorMac::GetAverageWatts(EnergyType type) const {
|
| + DCHECK_EQ(target_sample_count_, samples_[type].watts.size());
|
| + double sum = 0;
|
| + for (double v : samples_[type].watts)
|
| + sum += v;
|
| + return sum / samples_[type].watts.size();
|
| +}
|
| +
|
| +double EnergyMonitorMac::GetStdDevWatts(EnergyType type) const {
|
| + DCHECK_EQ(target_sample_count_, samples_[type].watts.size());
|
| + const double mean = GetAverageWatts(type);
|
| + double sum = 0;
|
| + for (double v : samples_[type].watts) {
|
| + v -= mean;
|
| + sum += v * v;
|
| + }
|
| + return sqrt(sum / (samples_[type].watts.size() - 1));
|
| +}
|
| +
|
| +void EnergyMonitorMac::WriteTimeSeries(const base::FilePath& file) {
|
| + DCHECK_EQ(target_sample_count_, rdtsc_values_.size());
|
| + DCHECK_EQ(target_sample_count_, samples_[PROCESSOR].watts.size());
|
| + DCHECK_EQ(target_sample_count_, samples_[IA].watts.size());
|
| +
|
| + std::ostringstream csv;
|
| + csv << "RDTSC,Processor_Watts,IA_Watts\n";
|
| + for (size_t i = 0; i < rdtsc_values_.size(); ++i) {
|
| + csv << rdtsc_values_[i] - rdtsc_values_[0]; // Always relative to start.
|
| + for (int j = 0; j < NUM_ENERGY_TYPES; ++j)
|
| + csv << ',' << samples_[j].watts[i];
|
| + csv << '\n';
|
| + }
|
| +
|
| + WriteFile(file, csv.str().data(), csv.str().size());
|
| +}
|
| +
|
| +void EnergyMonitorMac::ReadNextSample() {
|
| + CheckResult(ReadSample());
|
| + if (samples_[0].watts.size() + 1 >= target_sample_count_) {
|
| + MessageLoop::current()->PostTask(FROM_HERE, quit_closure_);
|
| + return;
|
| + }
|
| + MessageLoop::current()->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&EnergyMonitorMac::TakeSample, base::Unretained(this)),
|
| + sample_delay_);
|
| +}
|
| +
|
| +void EnergyMonitorMac::TakeSample() {
|
| + ReadNextSample();
|
| +
|
| + const int node = 0;
|
| + for (int i = 0; i < NUM_ENERGY_TYPES; ++i) {
|
| + int num_data;
|
| + double data[3];
|
| +
|
| + CheckResult(GetPowerData(node, samples_[i].msr_index, data, &num_data));
|
| + DCHECK_EQ(static_cast<int>(arraysize(data)), num_data);
|
| +
|
| + samples_[i].watts.push_back(data[POWER_DATA_AVERAGE_WATTS]);
|
| + }
|
| + uint64_t timestamp;
|
| + CheckResult(GetRDTSC(×tamp));
|
| + rdtsc_values_.push_back(timestamp);
|
| +}
|
| +
|
| +} // namespace test
|
| +} // namespace base
|
|
|