| /* |
| * Copyright (C) 2019 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.helpers; |
| |
| import android.os.SystemClock; |
| import android.util.Log; |
| |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.uiautomator.UiDevice; |
| |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** An {@link ProcLoadHelper} to check for cpu load in last minute is lesser or equal |
| * to given threshold for a given timeout and collect the cpu load as metric. |
| */ |
| public class ProcLoadHelper implements ICollectorHelper<Double> { |
| |
| private static final String LOG_TAG = ProcLoadHelper.class.getSimpleName(); |
| private static final String LOAD_CMD = "cat /proc/loadavg"; |
| public static final String LAST_MINUTE_LOAD_METRIC_KEY = "proc_loadavg_last_minute"; |
| public static final String PROC_LOAD_WAIT_TIME_METRIC_KEY = "proc_load_wait_time_msecs"; |
| |
| private static final Pattern LOAD_OUTPUT_PATTERN = Pattern.compile( |
| "(?<LASTMINUTELOAD>.*)\\s.*\\s.*\\s.*\\s.*"); |
| |
| private double mProcLoadThreshold = 0; |
| private long mProcLoadWaitTimeInMs = 0; |
| // Default to 500 msecs timeout. |
| private long mProcLoadIntervalInMs = 500; |
| private double mRecentLoad = 0; |
| private UiDevice mDevice; |
| private double mTotalWaitTime = 0; |
| |
| /** Wait untill the proc/load reaches below the threshold or timeout expires */ |
| @Override |
| public boolean startCollecting() { |
| mRecentLoad = 0; |
| long remainingWaitTime = mProcLoadWaitTimeInMs; |
| while (true) { |
| mRecentLoad = getProcLoadInLastMinute(); |
| Log.i(LOG_TAG, String.format("Average cpu load in last minute is : %s", mRecentLoad)); |
| if (mRecentLoad <= mProcLoadThreshold) { |
| break; |
| } else { |
| if (remainingWaitTime <= 0) { |
| Log.i(LOG_TAG, "Timeout because proc/loadavg never went below the threshold."); |
| return false; |
| } |
| long currWaitTime = (mProcLoadIntervalInMs < remainingWaitTime) |
| ? mProcLoadIntervalInMs : remainingWaitTime; |
| Log.d(LOG_TAG, String.format("Waiting for %s msecs", currWaitTime)); |
| SystemClock.sleep(currWaitTime); |
| mTotalWaitTime += currWaitTime; |
| Log.d(LOG_TAG, String.format("Waited for %s msecs", mTotalWaitTime)); |
| remainingWaitTime = remainingWaitTime - mProcLoadIntervalInMs; |
| } |
| } |
| return true; |
| } |
| |
| /** Collect the proc/load_avg last minute cpu load average metric. */ |
| @Override |
| public Map<String, Double> getMetrics() { |
| // Adding the last recorded load in the metric that will be reported. |
| Map<String, Double> result = new HashMap<>(); |
| Log.i(LOG_TAG, String.format("proc/loadavg in last minute before test is : %s", |
| mRecentLoad)); |
| result.put(LAST_MINUTE_LOAD_METRIC_KEY, mRecentLoad); |
| result.put(PROC_LOAD_WAIT_TIME_METRIC_KEY, mTotalWaitTime); |
| return result; |
| } |
| |
| /** Do nothing, because nothing is needed to disable cpuy load avg. */ |
| @Override |
| public boolean stopCollecting() { |
| return true; |
| } |
| |
| /** |
| * Parse the last minute cpu load from proc/loadavg |
| * |
| * @return cpu load in last minute. Returns -1 in case if it is failed to parse. |
| */ |
| private double getProcLoadInLastMinute() { |
| try { |
| String output = getDevice().executeShellCommand(LOAD_CMD); |
| Log.i(LOG_TAG, String.format("Output of proc_loadavg is : %s", output)); |
| // Output of the load command |
| // 1.39 1.10 1.21 2/2679 6380 |
| // 1.39 is the proc load in the last minute. |
| |
| Matcher match = null; |
| if ((match = matches(LOAD_OUTPUT_PATTERN, output.trim())) != null) { |
| Log.i(LOG_TAG, String.format("Current load is : %s", |
| match.group("LASTMINUTELOAD"))); |
| return Double.parseDouble(match.group("LASTMINUTELOAD")); |
| } else { |
| Log.w(LOG_TAG, "Not able to parse the proc/loadavg"); |
| } |
| } catch (IOException e) { |
| throw new RuntimeException("Failed to get proc/loadavg.", e); |
| } |
| |
| return -1; |
| } |
| |
| /** Returns the {@link UiDevice} under test. */ |
| private UiDevice getDevice() { |
| if (mDevice == null) { |
| mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); |
| } |
| return mDevice; |
| } |
| |
| /** |
| * Checks whether {@code line} matches the given {@link Pattern}. |
| * |
| * @return The resulting {@link Matcher} obtained by matching the {@code line} against |
| * {@code pattern}, or null if the {@code line} does not match. |
| */ |
| private static Matcher matches(Pattern pattern, String line) { |
| Matcher ret = pattern.matcher(line); |
| return ret.matches() ? ret : null; |
| } |
| |
| /** |
| * Sets the threshold value which the device cpu load average should be lesser than or equal. |
| */ |
| public void setProcLoadThreshold(double procLoadThreshold) { |
| mProcLoadThreshold = procLoadThreshold; |
| } |
| |
| /** |
| * Sets the timeout in msecs checking for threshold before proceeding with the testing. |
| */ |
| public void setProcLoadWaitTimeInMs(long procLoadWaitTimeInMs) { |
| mProcLoadWaitTimeInMs = procLoadWaitTimeInMs; |
| } |
| |
| /** |
| * Sets the interval time in msecs to check continuosly untill the timeout expires. |
| */ |
| public void setProcLoadIntervalInMs(long procLoadIntervalInMs) { |
| mProcLoadIntervalInMs = procLoadIntervalInMs; |
| } |
| } |