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 |