1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.tradefed.targetprep; 17 18 import com.android.tradefed.config.Option; 19 import com.android.tradefed.config.OptionClass; 20 import com.android.tradefed.device.DeviceNotAvailableException; 21 import com.android.tradefed.device.ITestDevice; 22 import com.android.tradefed.invoker.TestInformation; 23 import com.android.tradefed.log.LogUtil.CLog; 24 import com.android.tradefed.util.RunUtil; 25 26 import java.util.ArrayList; 27 import java.util.HashMap; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.Map.Entry; 31 32 /** 33 * An {@link ITargetPreparer} that waits until max frequency on all cores are restored to highest 34 * level available 35 */ 36 @OptionClass(alias = "cpu-throttle-waiter") 37 public class CpuThrottlingWaiter extends BaseTargetPreparer { 38 39 @Option(name = "poll-interval", 40 description = "Interval in seconds, to poll for core frequencies; defaults to 5s") 41 private long mPollIntervalSecs = 5; 42 43 @Option(name = "max-wait", description = "Max wait time in seconds, for all cores to be" 44 + " free of throttling; defaults to 240s") 45 private long mMaxWaitSecs = 240; 46 47 @Option(name = "post-idle-wait", description = "Additional time to wait in seconds, after cores" 48 + " are no longer subject to throttling; defaults to 120s") 49 private long mPostIdleWaitSecs = 120; 50 51 @Option(name = "abort-on-timeout", description = "If test should be aborted if cores are still" 52 + " subject to throttling after timeout has reached; defaults to false") 53 private boolean mAbortOnTimeout = false; 54 55 /** {@inheritDoc} */ 56 @Override setUp(TestInformation testInfo)57 public void setUp(TestInformation testInfo) 58 throws TargetSetupError, BuildError, DeviceNotAvailableException { 59 ITestDevice device = testInfo.getDevice(); 60 // first figure out number of CPU cores available and their corresponding max frequencies 61 // map: path/to/core : max frequency 62 Map<String, String> cpuMaxFreqs = getCpuMaxFreqs(device); 63 if (cpuMaxFreqs.isEmpty()) { 64 CLog.i("Unable to determine cores available, falling back to max wait time"); 65 RunUtil.getDefault().sleep(mMaxWaitSecs * 1000); 66 return; 67 } 68 // poll CPU frequencies 69 long start = System.currentTimeMillis(); 70 long maxWaitMs = mMaxWaitSecs * 1000; 71 long intervalMs = mPollIntervalSecs * 1000; 72 while (true) { 73 boolean ready = true; 74 for (Entry<String, String> e : cpuMaxFreqs.entrySet()) { 75 // check current frequency of each CPU 76 String freq = device.executeShellCommand( 77 String.format("cat %s/cpuinfo_max_freq", e.getKey())).trim(); 78 if (!e.getValue().equals(freq)) { 79 // not ready 80 CLog.d("CPU %s not ready: %s/%s", e.getKey(), freq, e.getValue()); 81 ready = false; 82 break; // for loop 83 } 84 } 85 if (ready) { 86 break; // while loop 87 } 88 if ((System.currentTimeMillis() - start) > maxWaitMs) { 89 CLog.w("cores still throttled after %ds", maxWaitMs); 90 String result = device.executeShellCommand( 91 "cat /sys/devices/system/cpu/*/cpufreq/cpuinfo_max_freq"); 92 CLog.w("Current CPU frequencies:\n%s", result); 93 if (mAbortOnTimeout) { 94 throw new TargetSetupError("cores are still throttled after wait timeout", 95 device.getDeviceDescriptor()); 96 } 97 break; // while loop 98 } 99 RunUtil.getDefault().sleep(intervalMs); 100 } 101 // extra idle time so that in case of thermal related throttling, allow heat to dissipate 102 RunUtil.getDefault().sleep(mPostIdleWaitSecs * 1000); 103 CLog.i("Done waiting, total time elapsed: %ds", 104 (System.currentTimeMillis() - start) / 1000); 105 } 106 107 /** 108 * Reads info under /sys/devices/system/cpu to determine cores available, and max frequencies 109 * possible for each core 110 * @param device device under test 111 * @return a {@link Map} with paths to sysfs cpuinfo as key, and corresponding max frequency as 112 * value 113 * @throws DeviceNotAvailableException 114 */ getCpuMaxFreqs(ITestDevice device)115 protected Map<String, String> getCpuMaxFreqs(ITestDevice device) 116 throws DeviceNotAvailableException { 117 Map<String, String> ret = new HashMap<>(); 118 String result = device.executeShellCommand("ls -1 -d /sys/devices/system/cpu/cpu*/cpufreq"); 119 String[] lines = result.split("\r?\n"); 120 List<String> cpuPaths = new ArrayList<>(); 121 for (String line : lines) { 122 cpuPaths.add(line.trim()); 123 } 124 for (String cpu : cpuPaths) { 125 result = device.executeShellCommand( 126 String.format("cat %s/scaling_available_frequencies", cpu)).trim(); 127 // return should be something like: 128 // 300000 422400 652800 729600 883200 960000 1036800 1190400 1267200 1497600 1574400 129 String[] freqs = result.split("\\s+"); 130 String maxFreq = freqs[freqs.length - 1]; // highest available frequency is last 131 // check if the string is a number 132 if (!maxFreq.matches("^\\d+$")) { 133 CLog.w("Unable to determine max frequency available for CPU: %s", cpu); 134 } else { 135 CLog.d("CPU: %s MaxFreq: %s", cpu, maxFreq); 136 ret.put(cpu, maxFreq); 137 } 138 } 139 return ret; 140 } 141 } 142