| /* |
| * 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 -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; |
| } |
| } |