blob: a9aae9dfcefae5f3006f87b7230ba6629adb136f [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.targetprep;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.util.RunUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* An {@link ITargetPreparer} that waits until max frequency on all cores are restored to highest
* level available
*
*/
@OptionClass(alias = "cpu-throttle-waiter")
public class CpuThrottlingWaiter implements ITargetPreparer {
@Option(name = "poll-interval",
description = "Interval in seconds, to poll for core frequencies; defaults to 5s")
private long mPollIntervalSecs = 5;
@Option(name = "max-wait", description = "Max wait time in seconds, for all cores to be"
+ " free of throttling; defaults to 240s")
private long mMaxWaitSecs = 240;
@Option(name = "post-idle-wait", description = "Additional time to wait in seconds, after cores"
+ " are no longer subject to throttling; defaults to 120s")
private long mPostIdleWaitSecs = 120;
@Option(name = "abort-on-timeout", description = "If test should be aborted if cores are still"
+ " subject to throttling after timeout has reached; defaults to false")
private boolean mAbortOnTimeout = false;
/**
* {@inheritDoc}
*/
@Override
public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
BuildError, DeviceNotAvailableException {
// first figure out number of CPU cores available and their corresponding max frequencies
// map: path/to/core : max frequency
Map<String, String> cpuMaxFreqs = getCpuMaxFreqs(device);
if (cpuMaxFreqs.isEmpty()) {
CLog.i("Unable to determine cores available, falling back to max wait time");
RunUtil.getDefault().sleep(mMaxWaitSecs * 1000);
return;
}
// poll CPU frequencies
long start = System.currentTimeMillis();
long maxWaitMs = mMaxWaitSecs * 1000;
long intervalMs = mPollIntervalSecs * 1000;
while (true) {
boolean ready = true;
for (Entry<String, String> e : cpuMaxFreqs.entrySet()) {
// check current frequency of each CPU
String freq = device.executeShellCommand(
String.format("cat %s/cpuinfo_max_freq", e.getKey())).trim();
if (!e.getValue().equals(freq)) {
// not ready
CLog.d("CPU %s not ready: %s/%s", e.getKey(), freq, e.getValue());
ready = false;
break; // for loop
}
}
if (ready) {
break; // while loop
}
if ((System.currentTimeMillis() - start) > maxWaitMs) {
CLog.w("cores still throttled after %ds", maxWaitMs);
String result = device.executeShellCommand(
"cat /sys/devices/system/cpu/*/cpufreq/cpuinfo_max_freq");
CLog.w("Current CPU frequencies:\n%s", result);
if (mAbortOnTimeout) {
throw new TargetSetupError("cores are still throttled after wait timeout");
}
break; // while loop
}
RunUtil.getDefault().sleep(intervalMs);
}
// extra idle time so that in case of thermal related throttling, allow heat to dissipate
RunUtil.getDefault().sleep(mPostIdleWaitSecs * 1000);
CLog.i("Done waiting, total time elapsed: %ds",
(System.currentTimeMillis() - start) / 1000);
}
/**
* Reads info under /sys/devices/system/cpu to determine cores available, and max frequencies
* possible for each core
* @param device device under test
* @return a {@link Map} with paths to sysfs cpuinfo as key, and corresponding max frequency as
* value
* @throws DeviceNotAvailableException
*/
protected Map<String, String> getCpuMaxFreqs(ITestDevice device)
throws DeviceNotAvailableException {
Map<String, String> ret = new HashMap<>();
String result = device.executeShellCommand("ls -1 -d /sys/devices/system/cpu/cpu*/cpufreq");
String[] lines = result.split("\r?\n");
List<String> cpuPaths = new ArrayList<>();
for (String line : lines) {
cpuPaths.add(line.trim());
}
for (String cpu : cpuPaths) {
result = device.executeShellCommand(
String.format("cat %s/scaling_available_frequencies", cpu)).trim();
// return should be something like:
// 300000 422400 652800 729600 883200 960000 1036800 1190400 1267200 1497600 1574400
String[] freqs = result.split("\\s+");
String maxFreq = freqs[freqs.length - 1]; // highest available frequency is last
// check if the string is a number
if (!maxFreq.matches("^\\d+$")) {
CLog.w("Unable to determine max frequency available for CPU: %s", cpu);
} else {
CLog.d("CPU: %s MaxFreq: %s", cpu, maxFreq);
ret.put(cpu, maxFreq);
}
}
return ret;
}
}