OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/test/energy_monitor_mac.h" |
| 6 |
| 7 #include <IntelPowerGadget/EnergyLib.h> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" |
| 11 #include "base/files/file_util.h" |
| 12 #include "base/logging.h" |
| 13 #include "base/run_loop.h" |
| 14 |
| 15 namespace base { |
| 16 namespace test { |
| 17 namespace { |
| 18 |
| 19 enum { |
| 20 POWER_DATA_AVERAGE_WATTS = 0, |
| 21 POWER_DATA_CUMULATIVE_JOULES = 1, |
| 22 POWER_DATA_CUMULATIVE_MILLIWATT_HOURS = 2 |
| 23 }; |
| 24 |
| 25 const int kDefaultDelayMs = 50; |
| 26 |
| 27 void CheckResult(int result) { |
| 28 CHECK_NE(0, result) << "Intel API call failed."; |
| 29 } |
| 30 |
| 31 } // namespace |
| 32 |
| 33 EnergyMonitorMac::EnergyMonitorMac() |
| 34 : sample_delay_(base::TimeDelta::FromMilliseconds(kDefaultDelayMs)) { |
| 35 static int initialize_once = IntelEnergyLibInitialize(); |
| 36 CHECK_NE(0, initialize_once) << "Failed to initialize."; |
| 37 |
| 38 int num_nodes = 0; |
| 39 CheckResult(GetNumNodes(&num_nodes)); |
| 40 CHECK_EQ(1, num_nodes) << "Only supports 1 node for now."; |
| 41 |
| 42 int num_msrs = 0; |
| 43 CheckResult(GetNumMsrs(&num_msrs)); |
| 44 |
| 45 for (int msr = 0; msr < num_msrs; ++msr) { |
| 46 int msr_func = 0; |
| 47 CheckResult(GetMsrFunc(msr, &msr_func)); |
| 48 if (msr_func != MSR_FUNC_POWER) |
| 49 continue; // Ignore frequency and temperature MSRs. |
| 50 |
| 51 char msr_name[4096]; // The API doesn't document a max length for this. |
| 52 CheckResult(GetMsrName(msr, msr_name)); |
| 53 |
| 54 if (strcmp(msr_name, "IA") == 0) { |
| 55 DCHECK_EQ(-1, samples_[IA].msr_index) << "Multiple IA MSRs"; |
| 56 samples_[IA].msr_index = msr; |
| 57 } |
| 58 |
| 59 if (strcmp(msr_name, "Processor") == 0) { |
| 60 DCHECK_EQ(-1, samples_[PROCESSOR].msr_index) << "Multiple Processor MSRs"; |
| 61 samples_[PROCESSOR].msr_index = msr; |
| 62 } |
| 63 } |
| 64 } |
| 65 |
| 66 EnergyMonitorMac::~EnergyMonitorMac() {} |
| 67 |
| 68 void EnergyMonitorMac::Run(const base::TimeDelta& warmup_delay, |
| 69 size_t num_samples) { |
| 70 DCHECK_LT(1u, num_samples) << "Need at least 2 samples."; |
| 71 target_sample_count_ = num_samples; |
| 72 |
| 73 // First step is to read a sample, but record nothing. This allows |
| 74 // measurements such as watts to establish the first time interval. |
| 75 MessageLoop::current()->PostDelayedTask( |
| 76 FROM_HERE, |
| 77 base::Bind(&EnergyMonitorMac::ReadNextSample, base::Unretained(this)), |
| 78 warmup_delay); |
| 79 |
| 80 base::RunLoop run_loop; |
| 81 quit_closure_ = run_loop.QuitClosure(); |
| 82 run_loop.Run(); |
| 83 } |
| 84 |
| 85 void EnergyMonitorMac::SetSampleDelay(const base::TimeDelta& sample_delay) {} |
| 86 |
| 87 double EnergyMonitorMac::GetAverageWatts(EnergyType type) const { |
| 88 DCHECK_EQ(target_sample_count_, samples_[type].watts.size()); |
| 89 double sum = 0; |
| 90 for (double v : samples_[type].watts) |
| 91 sum += v; |
| 92 return sum / samples_[type].watts.size(); |
| 93 } |
| 94 |
| 95 double EnergyMonitorMac::GetStdDevWatts(EnergyType type) const { |
| 96 DCHECK_EQ(target_sample_count_, samples_[type].watts.size()); |
| 97 const double mean = GetAverageWatts(type); |
| 98 double sum = 0; |
| 99 for (double v : samples_[type].watts) { |
| 100 v -= mean; |
| 101 sum += v * v; |
| 102 } |
| 103 return sqrt(sum / (samples_[type].watts.size() - 1)); |
| 104 } |
| 105 |
| 106 void EnergyMonitorMac::WriteTimeSeries(const base::FilePath& file) { |
| 107 DCHECK_EQ(target_sample_count_, rdtsc_values_.size()); |
| 108 DCHECK_EQ(target_sample_count_, samples_[PROCESSOR].watts.size()); |
| 109 DCHECK_EQ(target_sample_count_, samples_[IA].watts.size()); |
| 110 |
| 111 std::ostringstream csv; |
| 112 csv << "RDTSC,Processor_Watts,IA_Watts\n"; |
| 113 for (size_t i = 0; i < rdtsc_values_.size(); ++i) { |
| 114 csv << rdtsc_values_[i] - rdtsc_values_[0]; // Always relative to start. |
| 115 for (int j = 0; j < NUM_ENERGY_TYPES; ++j) |
| 116 csv << ',' << samples_[j].watts[i]; |
| 117 csv << '\n'; |
| 118 } |
| 119 |
| 120 WriteFile(file, csv.str().data(), csv.str().size()); |
| 121 } |
| 122 |
| 123 void EnergyMonitorMac::ReadNextSample() { |
| 124 CheckResult(ReadSample()); |
| 125 if (samples_[0].watts.size() + 1 >= target_sample_count_) { |
| 126 MessageLoop::current()->PostTask(FROM_HERE, quit_closure_); |
| 127 return; |
| 128 } |
| 129 MessageLoop::current()->PostDelayedTask( |
| 130 FROM_HERE, |
| 131 base::Bind(&EnergyMonitorMac::TakeSample, base::Unretained(this)), |
| 132 sample_delay_); |
| 133 } |
| 134 |
| 135 void EnergyMonitorMac::TakeSample() { |
| 136 ReadNextSample(); |
| 137 |
| 138 const int node = 0; |
| 139 for (int i = 0; i < NUM_ENERGY_TYPES; ++i) { |
| 140 int num_data; |
| 141 double data[3]; |
| 142 |
| 143 CheckResult(GetPowerData(node, samples_[i].msr_index, data, &num_data)); |
| 144 DCHECK_EQ(static_cast<int>(arraysize(data)), num_data); |
| 145 |
| 146 samples_[i].watts.push_back(data[POWER_DATA_AVERAGE_WATTS]); |
| 147 } |
| 148 uint64_t timestamp; |
| 149 CheckResult(GetRDTSC(×tamp)); |
| 150 rdtsc_values_.push_back(timestamp); |
| 151 } |
| 152 |
| 153 } // namespace test |
| 154 } // namespace base |
OLD | NEW |