Use bpf data when available for per-UID cpu stats am: 57635c976b
Change-Id: I1cba7c38a846793d2e019a68ea246bdee1f3f36a
This commit is contained in:
202
core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java
Normal file
202
core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.internal.os;
|
||||||
|
|
||||||
|
import android.os.StrictMode;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.util.Slog;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads cpu time bpf maps.
|
||||||
|
*
|
||||||
|
* It is implemented as singletons for each separate set of per-UID times. Get___Instance() method
|
||||||
|
* returns the corresponding reader instance. In order to prevent frequent GC, it reuses the same
|
||||||
|
* SparseArray to store data read from BPF maps.
|
||||||
|
*
|
||||||
|
* A KernelCpuUidBpfMapReader instance keeps an error counter. When the number of read errors within
|
||||||
|
* that instance accumulates to 5, this instance will reject all further read requests.
|
||||||
|
*
|
||||||
|
* Data fetched within last 500ms is considered fresh, since the reading lifecycle can take up to
|
||||||
|
* 25ms. KernelCpuUidBpfMapReader always tries to use cache if it is fresh and valid, but it can
|
||||||
|
* be disabled through a parameter.
|
||||||
|
*
|
||||||
|
* A KernelCpuUidBpfMapReader instance is thread-safe. It acquires a write lock when reading the bpf
|
||||||
|
* map, releases it right after, then acquires a read lock before returning a BpfMapIterator. Caller
|
||||||
|
* is responsible for closing BpfMapIterator (also auto-closable) after reading, otherwise deadlock
|
||||||
|
* will occur.
|
||||||
|
*/
|
||||||
|
public abstract class KernelCpuUidBpfMapReader {
|
||||||
|
private static final int ERROR_THRESHOLD = 5;
|
||||||
|
private static final long FRESHNESS_MS = 500L;
|
||||||
|
|
||||||
|
private static final KernelCpuUidBpfMapReader FREQ_TIME_READER =
|
||||||
|
new KernelCpuUidFreqTimeBpfMapReader();
|
||||||
|
|
||||||
|
private static final KernelCpuUidBpfMapReader ACTIVE_TIME_READER =
|
||||||
|
new KernelCpuUidActiveTimeBpfMapReader();
|
||||||
|
|
||||||
|
private static final KernelCpuUidBpfMapReader CLUSTER_TIME_READER =
|
||||||
|
new KernelCpuUidClusterTimeBpfMapReader();
|
||||||
|
|
||||||
|
static KernelCpuUidBpfMapReader getFreqTimeReaderInstance() {
|
||||||
|
return FREQ_TIME_READER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KernelCpuUidBpfMapReader getActiveTimeReaderInstance() {
|
||||||
|
return ACTIVE_TIME_READER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KernelCpuUidBpfMapReader getClusterTimeReaderInstance() {
|
||||||
|
return CLUSTER_TIME_READER;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String mTag = this.getClass().getSimpleName();
|
||||||
|
private int mErrors = 0;
|
||||||
|
private boolean mTracking = false;
|
||||||
|
protected SparseArray<long[]> mData = new SparseArray<>();
|
||||||
|
private long mLastReadTime = 0;
|
||||||
|
protected final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
|
||||||
|
protected final ReentrantReadWriteLock.ReadLock mReadLock = mLock.readLock();
|
||||||
|
protected final ReentrantReadWriteLock.WriteLock mWriteLock = mLock.writeLock();
|
||||||
|
|
||||||
|
public native boolean startTrackingBpfTimes();
|
||||||
|
|
||||||
|
protected abstract boolean readBpfData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of metadata used to inform the caller of 1) the size of array required by
|
||||||
|
* getNextUid and 2) how to interpret the raw data copied to that array.
|
||||||
|
*/
|
||||||
|
public abstract long[] getDataDimensions();
|
||||||
|
|
||||||
|
public void removeUidsInRange(int startUid, int endUid) {
|
||||||
|
if (mErrors > ERROR_THRESHOLD) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mWriteLock.lock();
|
||||||
|
int firstIndex = mData.indexOfKey(startUid);
|
||||||
|
int lastIndex = mData.indexOfKey(endUid);
|
||||||
|
mData.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
|
||||||
|
mWriteLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BpfMapIterator open() {
|
||||||
|
return open(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BpfMapIterator open(boolean ignoreCache) {
|
||||||
|
if (mErrors > ERROR_THRESHOLD) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!mTracking && !startTrackingBpfTimes()) {
|
||||||
|
Slog.w(mTag, "Failed to start tracking");
|
||||||
|
mErrors++;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (ignoreCache) {
|
||||||
|
mWriteLock.lock();
|
||||||
|
} else {
|
||||||
|
mReadLock.lock();
|
||||||
|
if (dataValid()) {
|
||||||
|
return new BpfMapIterator();
|
||||||
|
}
|
||||||
|
mReadLock.unlock();
|
||||||
|
mWriteLock.lock();
|
||||||
|
if (dataValid()) {
|
||||||
|
mReadLock.lock();
|
||||||
|
mWriteLock.unlock();
|
||||||
|
return new BpfMapIterator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (readBpfData()) {
|
||||||
|
mLastReadTime = SystemClock.elapsedRealtime();
|
||||||
|
mReadLock.lock();
|
||||||
|
mWriteLock.unlock();
|
||||||
|
return new BpfMapIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
mWriteLock.unlock();
|
||||||
|
mErrors++;
|
||||||
|
Slog.w(mTag, "Failed to read bpf times");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean dataValid() {
|
||||||
|
return mData.size() > 0 && (SystemClock.elapsedRealtime() - mLastReadTime < FRESHNESS_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BpfMapIterator implements AutoCloseable {
|
||||||
|
private int mPos;
|
||||||
|
|
||||||
|
public BpfMapIterator() {
|
||||||
|
};
|
||||||
|
|
||||||
|
public boolean getNextUid(long[] buf) {
|
||||||
|
if (mPos >= mData.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buf[0] = mData.keyAt(mPos);
|
||||||
|
System.arraycopy(mData.valueAt(mPos), 0, buf, 1, mData.valueAt(mPos).length);
|
||||||
|
mPos++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
mReadLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class KernelCpuUidFreqTimeBpfMapReader extends KernelCpuUidBpfMapReader {
|
||||||
|
|
||||||
|
private final native boolean removeUidRange(int startUid, int endUid);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final native boolean readBpfData();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final native long[] getDataDimensions();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUidsInRange(int startUid, int endUid) {
|
||||||
|
mWriteLock.lock();
|
||||||
|
super.removeUidsInRange(startUid, endUid);
|
||||||
|
removeUidRange(startUid, endUid);
|
||||||
|
mWriteLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class KernelCpuUidActiveTimeBpfMapReader extends KernelCpuUidBpfMapReader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final native boolean readBpfData();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final native long[] getDataDimensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class KernelCpuUidClusterTimeBpfMapReader extends KernelCpuUidBpfMapReader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final native boolean readBpfData();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final native long[] getDataDimensions();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import android.util.SparseArray;
|
|||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
|
import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
|
||||||
|
import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
@@ -57,6 +58,8 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
final SparseArray<T> mLastTimes = new SparseArray<>();
|
final SparseArray<T> mLastTimes = new SparseArray<>();
|
||||||
final KernelCpuProcStringReader mReader;
|
final KernelCpuProcStringReader mReader;
|
||||||
final boolean mThrottle;
|
final boolean mThrottle;
|
||||||
|
protected boolean mBpfTimesAvailable;
|
||||||
|
final KernelCpuUidBpfMapReader mBpfReader;
|
||||||
private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
|
private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
|
||||||
private long mLastReadTimeMs = 0;
|
private long mLastReadTimeMs = 0;
|
||||||
|
|
||||||
@@ -73,9 +76,15 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
void onUidCpuTime(int uid, T time);
|
void onUidCpuTime(int uid, T time);
|
||||||
}
|
}
|
||||||
|
|
||||||
KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
|
KernelCpuUidTimeReader(KernelCpuProcStringReader reader, @Nullable KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
|
||||||
mReader = reader;
|
mReader = reader;
|
||||||
mThrottle = throttle;
|
mThrottle = throttle;
|
||||||
|
mBpfReader = bpfReader;
|
||||||
|
mBpfTimesAvailable = (mBpfReader != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
|
||||||
|
this(reader, null, throttle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,9 +160,13 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
}
|
}
|
||||||
mLastTimes.put(startUid, null);
|
mLastTimes.put(startUid, null);
|
||||||
mLastTimes.put(endUid, null);
|
mLastTimes.put(endUid, null);
|
||||||
final int firstIndex = mLastTimes.indexOfKey(startUid);
|
int firstIndex = mLastTimes.indexOfKey(startUid);
|
||||||
final int lastIndex = mLastTimes.indexOfKey(endUid);
|
int lastIndex = mLastTimes.indexOfKey(endUid);
|
||||||
mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
|
mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
|
||||||
|
|
||||||
|
if (mBpfTimesAvailable) {
|
||||||
|
mBpfReader.removeUidsInRange(startUid, endUid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -323,13 +336,13 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
|
|
||||||
public KernelCpuUidFreqTimeReader(boolean throttle) {
|
public KernelCpuUidFreqTimeReader(boolean throttle) {
|
||||||
this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
|
this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
|
||||||
throttle);
|
KernelCpuUidBpfMapReader.getFreqTimeReaderInstance(), throttle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
|
public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
|
||||||
boolean throttle) {
|
KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
|
||||||
super(reader, throttle);
|
super(reader, bpfReader, throttle);
|
||||||
mProcFilePath = Paths.get(procFile);
|
mProcFilePath = Paths.get(procFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,19 +383,24 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
if (!mAllUidTimesAvailable) {
|
if (!mAllUidTimesAvailable) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final int oldMask = StrictMode.allowThreadDiskReadsMask();
|
if (mBpfTimesAvailable) {
|
||||||
try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
|
readFreqsThroughBpf();
|
||||||
if (readFreqs(reader.readLine()) == null) {
|
}
|
||||||
|
if (mCpuFreqs == null) {
|
||||||
|
final int oldMask = StrictMode.allowThreadDiskReadsMask();
|
||||||
|
try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
|
||||||
|
if (readFreqs(reader.readLine()) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (++mErrors >= MAX_ERROR_COUNT) {
|
||||||
|
mAllUidTimesAvailable = false;
|
||||||
|
}
|
||||||
|
Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
|
||||||
return null;
|
return null;
|
||||||
|
} finally {
|
||||||
|
StrictMode.setThreadPolicyMask(oldMask);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
|
||||||
if (++mErrors >= MAX_ERROR_COUNT) {
|
|
||||||
mAllUidTimesAvailable = false;
|
|
||||||
}
|
|
||||||
Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
StrictMode.setThreadPolicyMask(oldMask);
|
|
||||||
}
|
}
|
||||||
// Check if the freqs in the proc file correspond to per-cluster freqs.
|
// Check if the freqs in the proc file correspond to per-cluster freqs.
|
||||||
final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
|
final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
|
||||||
@@ -402,6 +420,21 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
return mCpuFreqs;
|
return mCpuFreqs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long[] readFreqsThroughBpf() {
|
||||||
|
if (!mBpfTimesAvailable || mBpfReader == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
mCpuFreqs = mBpfReader.getDataDimensions();
|
||||||
|
if (mCpuFreqs == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
mFreqCount = mCpuFreqs.length;
|
||||||
|
mCurTimes = new long[mFreqCount];
|
||||||
|
mDeltaTimes = new long[mFreqCount];
|
||||||
|
mBuffer = new long[mFreqCount + 1];
|
||||||
|
return mCpuFreqs;
|
||||||
|
}
|
||||||
|
|
||||||
private long[] readFreqs(String line) {
|
private long[] readFreqs(String line) {
|
||||||
if (line == null) {
|
if (line == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -422,8 +455,45 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
return mCpuFreqs;
|
return mCpuFreqs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processUidDelta(@Nullable Callback<long[]> cb) {
|
||||||
|
final int uid = (int) mBuffer[0];
|
||||||
|
long[] lastTimes = mLastTimes.get(uid);
|
||||||
|
if (lastTimes == null) {
|
||||||
|
lastTimes = new long[mFreqCount];
|
||||||
|
mLastTimes.put(uid, lastTimes);
|
||||||
|
}
|
||||||
|
copyToCurTimes();
|
||||||
|
boolean notify = false;
|
||||||
|
boolean valid = true;
|
||||||
|
for (int i = 0; i < mFreqCount; i++) {
|
||||||
|
// Unit is 10ms.
|
||||||
|
mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
|
||||||
|
if (mDeltaTimes[i] < 0) {
|
||||||
|
Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
notify |= mDeltaTimes[i] > 0;
|
||||||
|
}
|
||||||
|
if (notify && valid) {
|
||||||
|
System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
|
||||||
|
if (cb != null) {
|
||||||
|
cb.onUidCpuTime(uid, mDeltaTimes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void readDeltaImpl(@Nullable Callback<long[]> cb) {
|
void readDeltaImpl(@Nullable Callback<long[]> cb) {
|
||||||
|
if (mBpfTimesAvailable) {
|
||||||
|
try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
|
||||||
|
if (checkPrecondition(iter)) {
|
||||||
|
while (iter.getNextUid(mBuffer)) {
|
||||||
|
processUidDelta(cb);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
||||||
if (!checkPrecondition(iter)) {
|
if (!checkPrecondition(iter)) {
|
||||||
return;
|
return;
|
||||||
@@ -434,36 +504,24 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
Slog.wtf(mTag, "Invalid line: " + buf.toString());
|
Slog.wtf(mTag, "Invalid line: " + buf.toString());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final int uid = (int) mBuffer[0];
|
processUidDelta(cb);
|
||||||
long[] lastTimes = mLastTimes.get(uid);
|
|
||||||
if (lastTimes == null) {
|
|
||||||
lastTimes = new long[mFreqCount];
|
|
||||||
mLastTimes.put(uid, lastTimes);
|
|
||||||
}
|
|
||||||
copyToCurTimes();
|
|
||||||
boolean notify = false;
|
|
||||||
boolean valid = true;
|
|
||||||
for (int i = 0; i < mFreqCount; i++) {
|
|
||||||
// Unit is 10ms.
|
|
||||||
mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
|
|
||||||
if (mDeltaTimes[i] < 0) {
|
|
||||||
Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
notify |= mDeltaTimes[i] > 0;
|
|
||||||
}
|
|
||||||
if (notify && valid) {
|
|
||||||
System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
|
|
||||||
if (cb != null) {
|
|
||||||
cb.onUidCpuTime(uid, mDeltaTimes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void readAbsoluteImpl(Callback<long[]> cb) {
|
void readAbsoluteImpl(Callback<long[]> cb) {
|
||||||
|
if (mBpfTimesAvailable) {
|
||||||
|
try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
|
||||||
|
if (checkPrecondition(iter)) {
|
||||||
|
while (iter.getNextUid(mBuffer)) {
|
||||||
|
copyToCurTimes();
|
||||||
|
cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
||||||
if (!checkPrecondition(iter)) {
|
if (!checkPrecondition(iter)) {
|
||||||
return;
|
return;
|
||||||
@@ -481,11 +539,24 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void copyToCurTimes() {
|
private void copyToCurTimes() {
|
||||||
|
long factor = mBpfTimesAvailable ? 1 : 10;
|
||||||
for (int i = 0; i < mFreqCount; i++) {
|
for (int i = 0; i < mFreqCount; i++) {
|
||||||
mCurTimes[i] = mBuffer[i + 1] * 10;
|
mCurTimes[i] = mBuffer[i + 1] * factor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkPrecondition(BpfMapIterator iter) {
|
||||||
|
if (iter == null) {
|
||||||
|
mBpfTimesAvailable = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (mCpuFreqs != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
mBpfTimesAvailable = (readFreqsThroughBpf() != null);
|
||||||
|
return mBpfTimesAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkPrecondition(ProcFileIterator iter) {
|
private boolean checkPrecondition(ProcFileIterator iter) {
|
||||||
if (iter == null || !iter.hasNextLine()) {
|
if (iter == null || !iter.hasNextLine()) {
|
||||||
// Error logged in KernelCpuProcStringReader.
|
// Error logged in KernelCpuProcStringReader.
|
||||||
@@ -544,16 +615,43 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
private long[] mBuffer;
|
private long[] mBuffer;
|
||||||
|
|
||||||
public KernelCpuUidActiveTimeReader(boolean throttle) {
|
public KernelCpuUidActiveTimeReader(boolean throttle) {
|
||||||
super(KernelCpuProcStringReader.getActiveTimeReaderInstance(), throttle);
|
super(KernelCpuProcStringReader.getActiveTimeReaderInstance(),
|
||||||
|
KernelCpuUidBpfMapReader.getActiveTimeReaderInstance(), throttle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
|
public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
|
||||||
super(reader, throttle);
|
super(reader, bpfReader, throttle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processUidDelta(@Nullable Callback<Long> cb) {
|
||||||
|
int uid = (int) mBuffer[0];
|
||||||
|
long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
|
||||||
|
if (cpuActiveTime > 0) {
|
||||||
|
long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
|
||||||
|
if (delta > 0) {
|
||||||
|
mLastTimes.put(uid, cpuActiveTime);
|
||||||
|
if (cb != null) {
|
||||||
|
cb.onUidCpuTime(uid, delta);
|
||||||
|
}
|
||||||
|
} else if (delta < 0) {
|
||||||
|
Slog.e(mTag, "Negative delta from active time proc: " + delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void readDeltaImpl(@Nullable Callback<Long> cb) {
|
void readDeltaImpl(@Nullable Callback<Long> cb) {
|
||||||
|
if (mBpfTimesAvailable) {
|
||||||
|
try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
|
||||||
|
if (checkPrecondition(iter)) {
|
||||||
|
while (iter.getNextUid(mBuffer)) {
|
||||||
|
processUidDelta(cb);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
||||||
if (!checkPrecondition(iter)) {
|
if (!checkPrecondition(iter)) {
|
||||||
return;
|
return;
|
||||||
@@ -564,25 +662,30 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
Slog.wtf(mTag, "Invalid line: " + buf.toString());
|
Slog.wtf(mTag, "Invalid line: " + buf.toString());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int uid = (int) mBuffer[0];
|
processUidDelta(cb);
|
||||||
long cpuActiveTime = sumActiveTime(mBuffer);
|
|
||||||
if (cpuActiveTime > 0) {
|
|
||||||
long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
|
|
||||||
if (delta > 0) {
|
|
||||||
mLastTimes.put(uid, cpuActiveTime);
|
|
||||||
if (cb != null) {
|
|
||||||
cb.onUidCpuTime(uid, delta);
|
|
||||||
}
|
|
||||||
} else if (delta < 0) {
|
|
||||||
Slog.e(mTag, "Negative delta from active time proc: " + delta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processUidAbsolute(@Nullable Callback<Long> cb) {
|
||||||
|
long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
|
||||||
|
if (cpuActiveTime > 0) {
|
||||||
|
cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void readAbsoluteImpl(Callback<Long> cb) {
|
void readAbsoluteImpl(Callback<Long> cb) {
|
||||||
|
if (mBpfTimesAvailable) {
|
||||||
|
try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
|
||||||
|
if (checkPrecondition(iter)) {
|
||||||
|
while (iter.getNextUid(mBuffer)) {
|
||||||
|
processUidAbsolute(cb);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
||||||
if (!checkPrecondition(iter)) {
|
if (!checkPrecondition(iter)) {
|
||||||
return;
|
return;
|
||||||
@@ -593,23 +696,38 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
Slog.wtf(mTag, "Invalid line: " + buf.toString());
|
Slog.wtf(mTag, "Invalid line: " + buf.toString());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
long cpuActiveTime = sumActiveTime(mBuffer);
|
processUidAbsolute(cb);
|
||||||
if (cpuActiveTime > 0) {
|
|
||||||
cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long sumActiveTime(long[] times) {
|
private static long sumActiveTime(long[] times, double factor) {
|
||||||
// UID is stored at times[0].
|
// UID is stored at times[0].
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
for (int i = 1; i < times.length; i++) {
|
for (int i = 1; i < times.length; i++) {
|
||||||
sum += (double) times[i] * 10 / i; // Unit is 10ms.
|
sum += (double) times[i] * factor / i; // Unit is 10ms.
|
||||||
}
|
}
|
||||||
return (long) sum;
|
return (long) sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkPrecondition(BpfMapIterator iter) {
|
||||||
|
if (iter == null) {
|
||||||
|
mBpfTimesAvailable = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (mCores > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
long[] cores = mBpfReader.getDataDimensions();
|
||||||
|
if (cores == null || cores.length < 1) {
|
||||||
|
mBpfTimesAvailable = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mCores = (int) cores[0];
|
||||||
|
mBuffer = new long[mCores + 1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkPrecondition(ProcFileIterator iter) {
|
private boolean checkPrecondition(ProcFileIterator iter) {
|
||||||
if (iter == null || !iter.hasNextLine()) {
|
if (iter == null || !iter.hasNextLine()) {
|
||||||
// Error logged in KernelCpuProcStringReader.
|
// Error logged in KernelCpuProcStringReader.
|
||||||
@@ -664,16 +782,54 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
private long[] mDeltaTime;
|
private long[] mDeltaTime;
|
||||||
|
|
||||||
public KernelCpuUidClusterTimeReader(boolean throttle) {
|
public KernelCpuUidClusterTimeReader(boolean throttle) {
|
||||||
super(KernelCpuProcStringReader.getClusterTimeReaderInstance(), throttle);
|
super(KernelCpuProcStringReader.getClusterTimeReaderInstance(),
|
||||||
|
KernelCpuUidBpfMapReader.getClusterTimeReaderInstance(), throttle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
|
public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader,
|
||||||
super(reader, throttle);
|
KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
|
||||||
|
super(reader, bpfReader, throttle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processUidDelta(@Nullable Callback<long[]> cb) {
|
||||||
|
int uid = (int) mBuffer[0];
|
||||||
|
long[] lastTimes = mLastTimes.get(uid);
|
||||||
|
if (lastTimes == null) {
|
||||||
|
lastTimes = new long[mNumClusters];
|
||||||
|
mLastTimes.put(uid, lastTimes);
|
||||||
|
}
|
||||||
|
sumClusterTime();
|
||||||
|
boolean valid = true;
|
||||||
|
boolean notify = false;
|
||||||
|
for (int i = 0; i < mNumClusters; i++) {
|
||||||
|
mDeltaTime[i] = mCurTime[i] - lastTimes[i];
|
||||||
|
if (mDeltaTime[i] < 0) {
|
||||||
|
Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
notify |= mDeltaTime[i] > 0;
|
||||||
|
}
|
||||||
|
if (notify && valid) {
|
||||||
|
System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
|
||||||
|
if (cb != null) {
|
||||||
|
cb.onUidCpuTime(uid, mDeltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void readDeltaImpl(@Nullable Callback<long[]> cb) {
|
void readDeltaImpl(@Nullable Callback<long[]> cb) {
|
||||||
|
if (mBpfTimesAvailable) {
|
||||||
|
try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
|
||||||
|
if (checkPrecondition(iter)) {
|
||||||
|
while (iter.getNextUid(mBuffer)) {
|
||||||
|
processUidDelta(cb);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
||||||
if (!checkPrecondition(iter)) {
|
if (!checkPrecondition(iter)) {
|
||||||
return;
|
return;
|
||||||
@@ -684,35 +840,24 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
Slog.wtf(mTag, "Invalid line: " + buf.toString());
|
Slog.wtf(mTag, "Invalid line: " + buf.toString());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int uid = (int) mBuffer[0];
|
processUidDelta(cb);
|
||||||
long[] lastTimes = mLastTimes.get(uid);
|
|
||||||
if (lastTimes == null) {
|
|
||||||
lastTimes = new long[mNumClusters];
|
|
||||||
mLastTimes.put(uid, lastTimes);
|
|
||||||
}
|
|
||||||
sumClusterTime();
|
|
||||||
boolean valid = true;
|
|
||||||
boolean notify = false;
|
|
||||||
for (int i = 0; i < mNumClusters; i++) {
|
|
||||||
mDeltaTime[i] = mCurTime[i] - lastTimes[i];
|
|
||||||
if (mDeltaTime[i] < 0) {
|
|
||||||
Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
notify |= mDeltaTime[i] > 0;
|
|
||||||
}
|
|
||||||
if (notify && valid) {
|
|
||||||
System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
|
|
||||||
if (cb != null) {
|
|
||||||
cb.onUidCpuTime(uid, mDeltaTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void readAbsoluteImpl(Callback<long[]> cb) {
|
void readAbsoluteImpl(Callback<long[]> cb) {
|
||||||
|
if (mBpfTimesAvailable) {
|
||||||
|
try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
|
||||||
|
if (checkPrecondition(iter)) {
|
||||||
|
while (iter.getNextUid(mBuffer)) {
|
||||||
|
sumClusterTime();
|
||||||
|
cb.onUidCpuTime((int) mBuffer[0], mCurTime);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
|
||||||
if (!checkPrecondition(iter)) {
|
if (!checkPrecondition(iter)) {
|
||||||
return;
|
return;
|
||||||
@@ -730,17 +875,45 @@ public abstract class KernelCpuUidTimeReader<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sumClusterTime() {
|
private void sumClusterTime() {
|
||||||
|
double factor = mBpfTimesAvailable ? 1 : 10;
|
||||||
// UID is stored at mBuffer[0].
|
// UID is stored at mBuffer[0].
|
||||||
int core = 1;
|
int core = 1;
|
||||||
for (int i = 0; i < mNumClusters; i++) {
|
for (int i = 0; i < mNumClusters; i++) {
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
for (int j = 1; j <= mCoresOnClusters[i]; j++) {
|
for (int j = 1; j <= mCoresOnClusters[i]; j++) {
|
||||||
sum += (double) mBuffer[core++] * 10 / j; // Unit is 10ms.
|
sum += (double) mBuffer[core++] * factor / j; // Unit is 10ms.
|
||||||
}
|
}
|
||||||
mCurTime[i] = (long) sum;
|
mCurTime[i] = (long) sum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkPrecondition(BpfMapIterator iter) {
|
||||||
|
if (iter == null) {
|
||||||
|
mBpfTimesAvailable = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (mNumClusters > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
long[] coresOnClusters = mBpfReader.getDataDimensions();
|
||||||
|
if (coresOnClusters == null || coresOnClusters.length < 1) {
|
||||||
|
mBpfTimesAvailable = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mNumClusters = coresOnClusters.length;
|
||||||
|
mCoresOnClusters = new int[mNumClusters];
|
||||||
|
int cores = 0;
|
||||||
|
for (int i = 0; i < mNumClusters; i++) {
|
||||||
|
mCoresOnClusters[i] = (int) coresOnClusters[i];
|
||||||
|
cores += mCoresOnClusters[i];
|
||||||
|
}
|
||||||
|
mNumCores = cores;
|
||||||
|
mBuffer = new long[cores + 1];
|
||||||
|
mCurTime = new long[mNumClusters];
|
||||||
|
mDeltaTime = new long[mNumClusters];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkPrecondition(ProcFileIterator iter) {
|
private boolean checkPrecondition(ProcFileIterator iter) {
|
||||||
if (iter == null || !iter.hasNextLine()) {
|
if (iter == null || !iter.hasNextLine()) {
|
||||||
// Error logged in KernelCpuProcStringReader.
|
// Error logged in KernelCpuProcStringReader.
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ cc_library_shared {
|
|||||||
"com_android_internal_os_AtomicDirectory.cpp",
|
"com_android_internal_os_AtomicDirectory.cpp",
|
||||||
"com_android_internal_os_ClassLoaderFactory.cpp",
|
"com_android_internal_os_ClassLoaderFactory.cpp",
|
||||||
"com_android_internal_os_FuseAppLoop.cpp",
|
"com_android_internal_os_FuseAppLoop.cpp",
|
||||||
|
"com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
|
||||||
"com_android_internal_os_Zygote.cpp",
|
"com_android_internal_os_Zygote.cpp",
|
||||||
"com_android_internal_os_ZygoteInit.cpp",
|
"com_android_internal_os_ZygoteInit.cpp",
|
||||||
"com_android_internal_util_VirtualRefBasePtr.cpp",
|
"com_android_internal_util_VirtualRefBasePtr.cpp",
|
||||||
@@ -303,6 +304,7 @@ cc_library_shared {
|
|||||||
"libdl",
|
"libdl",
|
||||||
"libdl_android",
|
"libdl_android",
|
||||||
"libstatslog",
|
"libstatslog",
|
||||||
|
"libtimeinstate",
|
||||||
"server_configurable_flags",
|
"server_configurable_flags",
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env
|
|||||||
extern int register_com_android_internal_os_AtomicDirectory(JNIEnv *env);
|
extern int register_com_android_internal_os_AtomicDirectory(JNIEnv *env);
|
||||||
extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
|
extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
|
||||||
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
|
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
|
||||||
|
extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
|
||||||
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
|
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
|
||||||
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
|
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
|
||||||
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
|
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
|
||||||
@@ -1640,6 +1641,7 @@ static const RegJNIRec gRegJNI[] = {
|
|||||||
REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
|
REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
|
||||||
REG_JNI(register_com_android_internal_os_AtomicDirectory),
|
REG_JNI(register_com_android_internal_os_AtomicDirectory),
|
||||||
REG_JNI(register_com_android_internal_os_FuseAppLoop),
|
REG_JNI(register_com_android_internal_os_FuseAppLoop),
|
||||||
|
REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader),
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
217
core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
Normal file
217
core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core_jni_helpers.h"
|
||||||
|
|
||||||
|
#include <sys/sysinfo.h>
|
||||||
|
|
||||||
|
#include <android-base/stringprintf.h>
|
||||||
|
#include <cputimeinstate.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
static constexpr uint64_t NSEC_PER_MSEC = 1000000;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
jclass clazz;
|
||||||
|
jmethodID put;
|
||||||
|
jmethodID get;
|
||||||
|
} gSparseArrayClassInfo;
|
||||||
|
|
||||||
|
static jfieldID gmData;
|
||||||
|
|
||||||
|
static jlongArray getUidArray(JNIEnv *env, jobject sparseAr, uint32_t uid, jsize sz) {
|
||||||
|
jlongArray ar = (jlongArray)env->CallObjectMethod(sparseAr, gSparseArrayClassInfo.get, uid);
|
||||||
|
if (!ar) {
|
||||||
|
ar = env->NewLongArray(sz);
|
||||||
|
if (ar == NULL) return ar;
|
||||||
|
env->CallVoidMethod(sparseAr, gSparseArrayClassInfo.put, uid, ar);
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy2DVecToArray(JNIEnv *env, jlongArray ar, std::vector<std::vector<uint64_t>> &vec) {
|
||||||
|
jsize start = 0;
|
||||||
|
for (auto &subVec : vec) {
|
||||||
|
for (uint32_t i = 0; i < subVec.size(); ++i) subVec[i] /= NSEC_PER_MSEC;
|
||||||
|
env->SetLongArrayRegion(ar, start, subVec.size(),
|
||||||
|
reinterpret_cast<const jlong *>(subVec.data()));
|
||||||
|
start += subVec.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static jboolean KernelCpuUidFreqTimeBpfMapReader_removeUidRange(JNIEnv *env, jclass, jint startUid,
|
||||||
|
jint endUid) {
|
||||||
|
for (uint32_t uid = startUid; uid <= endUid; ++uid) {
|
||||||
|
if (!android::bpf::clearUidTimes(uid)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jboolean KernelCpuUidFreqTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
|
||||||
|
static uint64_t lastUpdate = 0;
|
||||||
|
uint64_t newLastUpdate = lastUpdate;
|
||||||
|
auto sparseAr = env->GetObjectField(thiz, gmData);
|
||||||
|
if (sparseAr == NULL) return false;
|
||||||
|
auto data = android::bpf::getUidsUpdatedCpuFreqTimes(&newLastUpdate);
|
||||||
|
if (!data.has_value()) return false;
|
||||||
|
|
||||||
|
jsize s = 0;
|
||||||
|
for (auto &[uid, times] : *data) {
|
||||||
|
if (s == 0) {
|
||||||
|
for (const auto &subVec : times) s += subVec.size();
|
||||||
|
}
|
||||||
|
jlongArray ar = getUidArray(env, sparseAr, uid, s);
|
||||||
|
if (ar == NULL) return false;
|
||||||
|
copy2DVecToArray(env, ar, times);
|
||||||
|
}
|
||||||
|
lastUpdate = newLastUpdate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jlongArray KernelCpuUidFreqTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
|
||||||
|
auto freqs = android::bpf::getCpuFreqs();
|
||||||
|
if (!freqs) return NULL;
|
||||||
|
|
||||||
|
std::vector<uint64_t> allFreqs;
|
||||||
|
for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs));
|
||||||
|
|
||||||
|
auto ar = env->NewLongArray(allFreqs.size());
|
||||||
|
if (ar != NULL) {
|
||||||
|
env->SetLongArrayRegion(ar, 0, allFreqs.size(),
|
||||||
|
reinterpret_cast<const jlong *>(allFreqs.data()));
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const JNINativeMethod gFreqTimeMethods[] = {
|
||||||
|
{"removeUidRange", "(II)Z", (void *)KernelCpuUidFreqTimeBpfMapReader_removeUidRange},
|
||||||
|
{"readBpfData", "()Z", (void *)KernelCpuUidFreqTimeBpfMapReader_readBpfData},
|
||||||
|
{"getDataDimensions", "()[J", (void *)KernelCpuUidFreqTimeBpfMapReader_getDataDimensions},
|
||||||
|
};
|
||||||
|
|
||||||
|
static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
|
||||||
|
static uint64_t lastUpdate = 0;
|
||||||
|
uint64_t newLastUpdate = lastUpdate;
|
||||||
|
auto sparseAr = env->GetObjectField(thiz, gmData);
|
||||||
|
if (sparseAr == NULL) return false;
|
||||||
|
auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
|
||||||
|
if (!data.has_value()) return false;
|
||||||
|
|
||||||
|
for (auto &[uid, times] : *data) {
|
||||||
|
// TODO: revise calling code so we can divide by NSEC_PER_MSEC here instead
|
||||||
|
for (auto &time : times.active) time /= NSEC_PER_MSEC;
|
||||||
|
jlongArray ar = getUidArray(env, sparseAr, uid, times.active.size());
|
||||||
|
if (ar == NULL) return false;
|
||||||
|
env->SetLongArrayRegion(ar, 0, times.active.size(),
|
||||||
|
reinterpret_cast<const jlong *>(times.active.data()));
|
||||||
|
}
|
||||||
|
lastUpdate = newLastUpdate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jlongArray KernelCpuUidActiveTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
|
||||||
|
jlong nCpus = get_nprocs_conf();
|
||||||
|
|
||||||
|
auto ar = env->NewLongArray(1);
|
||||||
|
if (ar != NULL) env->SetLongArrayRegion(ar, 0, 1, &nCpus);
|
||||||
|
return ar;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const JNINativeMethod gActiveTimeMethods[] = {
|
||||||
|
{"readBpfData", "()Z", (void *)KernelCpuUidActiveTimeBpfMapReader_readBpfData},
|
||||||
|
{"getDataDimensions", "()[J", (void *)KernelCpuUidActiveTimeBpfMapReader_getDataDimensions},
|
||||||
|
};
|
||||||
|
|
||||||
|
static jboolean KernelCpuUidClusterTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
|
||||||
|
static uint64_t lastUpdate = 0;
|
||||||
|
uint64_t newLastUpdate = lastUpdate;
|
||||||
|
auto sparseAr = env->GetObjectField(thiz, gmData);
|
||||||
|
if (sparseAr == NULL) return false;
|
||||||
|
auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
|
||||||
|
if (!data.has_value()) return false;
|
||||||
|
|
||||||
|
jsize s = 0;
|
||||||
|
for (auto &[uid, times] : *data) {
|
||||||
|
if (s == 0) {
|
||||||
|
for (const auto &subVec : times.policy) s += subVec.size();
|
||||||
|
}
|
||||||
|
jlongArray ar = getUidArray(env, sparseAr, uid, s);
|
||||||
|
if (ar == NULL) return false;
|
||||||
|
copy2DVecToArray(env, ar, times.policy);
|
||||||
|
}
|
||||||
|
lastUpdate = newLastUpdate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jlongArray KernelCpuUidClusterTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
|
||||||
|
auto times = android::bpf::getUidConcurrentTimes(0);
|
||||||
|
if (!times.has_value()) return NULL;
|
||||||
|
|
||||||
|
std::vector<jlong> clusterCores;
|
||||||
|
for (const auto &vec : times->policy) clusterCores.push_back(vec.size());
|
||||||
|
auto ar = env->NewLongArray(clusterCores.size());
|
||||||
|
if (ar != NULL) env->SetLongArrayRegion(ar, 0, clusterCores.size(), clusterCores.data());
|
||||||
|
return ar;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const JNINativeMethod gClusterTimeMethods[] = {
|
||||||
|
{"readBpfData", "()Z", (void *)KernelCpuUidClusterTimeBpfMapReader_readBpfData},
|
||||||
|
{"getDataDimensions", "()[J",
|
||||||
|
(void *)KernelCpuUidClusterTimeBpfMapReader_getDataDimensions},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct readerMethods {
|
||||||
|
const char *name;
|
||||||
|
const JNINativeMethod *methods;
|
||||||
|
int numMethods;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const readerMethods gAllMethods[] = {
|
||||||
|
{"KernelCpuUidFreqTimeBpfMapReader", gFreqTimeMethods, NELEM(gFreqTimeMethods)},
|
||||||
|
{"KernelCpuUidActiveTimeBpfMapReader", gActiveTimeMethods, NELEM(gActiveTimeMethods)},
|
||||||
|
{"KernelCpuUidClusterTimeBpfMapReader", gClusterTimeMethods, NELEM(gClusterTimeMethods)},
|
||||||
|
};
|
||||||
|
|
||||||
|
static jboolean KernelCpuUidBpfMapReader_startTrackingBpfTimes(JNIEnv *, jobject) {
|
||||||
|
return android::bpf::startTrackingUidTimes();
|
||||||
|
}
|
||||||
|
|
||||||
|
int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) {
|
||||||
|
gSparseArrayClassInfo.clazz = FindClassOrDie(env, "android/util/SparseArray");
|
||||||
|
gSparseArrayClassInfo.clazz = MakeGlobalRefOrDie(env, gSparseArrayClassInfo.clazz);
|
||||||
|
gSparseArrayClassInfo.put =
|
||||||
|
GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "put", "(ILjava/lang/Object;)V");
|
||||||
|
gSparseArrayClassInfo.get =
|
||||||
|
GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "get", "(I)Ljava/lang/Object;");
|
||||||
|
constexpr auto readerName = "com/android/internal/os/KernelCpuUidBpfMapReader";
|
||||||
|
constexpr JNINativeMethod method = {"startTrackingBpfTimes", "()Z",
|
||||||
|
(void *)KernelCpuUidBpfMapReader_startTrackingBpfTimes};
|
||||||
|
|
||||||
|
int ret = RegisterMethodsOrDie(env, readerName, &method, 1);
|
||||||
|
if (ret < 0) return ret;
|
||||||
|
auto c = FindClassOrDie(env, readerName);
|
||||||
|
gmData = GetFieldIDOrDie(env, c, "mData", "Landroid/util/SparseArray;");
|
||||||
|
|
||||||
|
for (const auto &m : gAllMethods) {
|
||||||
|
auto fullName = android::base::StringPrintf("%s$%s", readerName, m.name);
|
||||||
|
ret = RegisterMethodsOrDie(env, fullName.c_str(), m.methods, m.numMethods);
|
||||||
|
if (ret < 0) break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace android
|
||||||
@@ -40,6 +40,7 @@ import org.junit.runners.Suite;
|
|||||||
BatteryStatsUserLifecycleTests.class,
|
BatteryStatsUserLifecycleTests.class,
|
||||||
KernelCpuProcStringReaderTest.class,
|
KernelCpuProcStringReaderTest.class,
|
||||||
KernelCpuUidActiveTimeReaderTest.class,
|
KernelCpuUidActiveTimeReaderTest.class,
|
||||||
|
KernelCpuUidBpfMapReaderTest.class,
|
||||||
KernelCpuUidClusterTimeReaderTest.class,
|
KernelCpuUidClusterTimeReaderTest.class,
|
||||||
KernelCpuUidFreqTimeReaderTest.class,
|
KernelCpuUidFreqTimeReaderTest.class,
|
||||||
KernelCpuUidUserSysTimeReaderTest.class,
|
KernelCpuUidUserSysTimeReaderTest.class,
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ import static org.junit.Assert.assertTrue;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.FileUtils;
|
import android.os.FileUtils;
|
||||||
|
import android.util.SparseArray;
|
||||||
import android.util.SparseLongArray;
|
import android.util.SparseLongArray;
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
import androidx.test.InstrumentationRegistry;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
|
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
|
||||||
|
|
||||||
@@ -33,11 +33,15 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,14 +50,16 @@ import java.util.Random;
|
|||||||
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest
|
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest
|
||||||
*/
|
*/
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(Parameterized.class)
|
||||||
public class KernelCpuUidActiveTimeReaderTest {
|
public class KernelCpuUidActiveTimeReaderTest {
|
||||||
private File mTestDir;
|
private File mTestDir;
|
||||||
private File mTestFile;
|
private File mTestFile;
|
||||||
private KernelCpuUidActiveTimeReader mReader;
|
private KernelCpuUidActiveTimeReader mReader;
|
||||||
|
private KernelCpuUidTestBpfMapReader mBpfMapReader;
|
||||||
private VerifiableCallback mCallback;
|
private VerifiableCallback mCallback;
|
||||||
|
|
||||||
private Random mRand = new Random(12345);
|
private Random mRand = new Random(12345);
|
||||||
|
protected boolean mUseBpf;
|
||||||
private final int mCpus = 4;
|
private final int mCpus = 4;
|
||||||
private final String mHeadline = "cpus: 4\n";
|
private final String mHeadline = "cpus: 4\n";
|
||||||
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
|
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
|
||||||
@@ -62,12 +68,22 @@ public class KernelCpuUidActiveTimeReaderTest {
|
|||||||
return InstrumentationRegistry.getContext();
|
return InstrumentationRegistry.getContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parameters(name="useBpf={0}")
|
||||||
|
public static Collection<Object[]> data() {
|
||||||
|
return Arrays.asList(new Object[][] { {true}, {false} });
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelCpuUidActiveTimeReaderTest(boolean useBpf) {
|
||||||
|
mUseBpf = useBpf;
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
|
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
|
||||||
mTestFile = new File(mTestDir, "test.file");
|
mTestFile = new File(mTestDir, "test.file");
|
||||||
|
mBpfMapReader = new KernelCpuUidTestBpfMapReader();
|
||||||
mReader = new KernelCpuUidActiveTimeReader(
|
mReader = new KernelCpuUidActiveTimeReader(
|
||||||
new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
|
new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
|
||||||
mCallback = new VerifiableCallback();
|
mCallback = new VerifiableCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +96,7 @@ public class KernelCpuUidActiveTimeReaderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testReadDelta() throws Exception {
|
public void testReadDelta() throws Exception {
|
||||||
final long[][] times = increaseTime(new long[mUids.length][mCpus]);
|
final long[][] times = increaseTime(new long[mUids.length][mCpus]);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times));
|
setCpusAndData(times);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 0; i < mUids.length; ++i) {
|
for (int i = 0; i < mUids.length; ++i) {
|
||||||
mCallback.verify(mUids[i], getActiveTime(times[i]));
|
mCallback.verify(mUids[i], getActiveTime(times[i]));
|
||||||
@@ -90,7 +106,7 @@ public class KernelCpuUidActiveTimeReaderTest {
|
|||||||
// Verify that a second call will only return deltas.
|
// Verify that a second call will only return deltas.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] newTimes1 = increaseTime(times);
|
final long[][] newTimes1 = increaseTime(times);
|
||||||
writeToFile(mHeadline + uidLines(mUids, newTimes1));
|
setCpusAndData(newTimes1);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 0; i < mUids.length; ++i) {
|
for (int i = 0; i < mUids.length; ++i) {
|
||||||
mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i]));
|
mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i]));
|
||||||
@@ -105,7 +121,7 @@ public class KernelCpuUidActiveTimeReaderTest {
|
|||||||
// Verify that calling with a null callback doesn't result in any crashes
|
// Verify that calling with a null callback doesn't result in any crashes
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] newTimes2 = increaseTime(newTimes1);
|
final long[][] newTimes2 = increaseTime(newTimes1);
|
||||||
writeToFile(mHeadline + uidLines(mUids, newTimes2));
|
setCpusAndData(newTimes2);
|
||||||
mReader.readDelta(null);
|
mReader.readDelta(null);
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
|
|
||||||
@@ -113,19 +129,20 @@ public class KernelCpuUidActiveTimeReaderTest {
|
|||||||
// the previous call had null callback.
|
// the previous call had null callback.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] newTimes3 = increaseTime(newTimes2);
|
final long[][] newTimes3 = increaseTime(newTimes2);
|
||||||
|
setCpusAndData(newTimes3);
|
||||||
writeToFile(mHeadline + uidLines(mUids, newTimes3));
|
writeToFile(mHeadline + uidLines(mUids, newTimes3));
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 0; i < mUids.length; ++i) {
|
for (int i = 0; i < mUids.length; ++i) {
|
||||||
mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i]));
|
mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i]));
|
||||||
}
|
}
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
assertTrue(mTestFile.delete());
|
clearCpusAndData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadAbsolute() throws Exception {
|
public void testReadAbsolute() throws Exception {
|
||||||
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times1));
|
setCpusAndData(times1);
|
||||||
mReader.readAbsolute(mCallback);
|
mReader.readAbsolute(mCallback);
|
||||||
for (int i = 0; i < mUids.length; i++) {
|
for (int i = 0; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], getActiveTime(times1[i]));
|
mCallback.verify(mUids[i], getActiveTime(times1[i]));
|
||||||
@@ -135,19 +152,19 @@ public class KernelCpuUidActiveTimeReaderTest {
|
|||||||
// Verify that a second call should still return absolute values
|
// Verify that a second call should still return absolute values
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times2 = increaseTime(times1);
|
final long[][] times2 = increaseTime(times1);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times2));
|
setCpusAndData(times2);
|
||||||
mReader.readAbsolute(mCallback);
|
mReader.readAbsolute(mCallback);
|
||||||
for (int i = 0; i < mUids.length; i++) {
|
for (int i = 0; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], getActiveTime(times2[i]));
|
mCallback.verify(mUids[i], getActiveTime(times2[i]));
|
||||||
}
|
}
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
assertTrue(mTestFile.delete());
|
clearCpusAndData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadDeltaDecreasedTime() throws Exception {
|
public void testReadDeltaDecreasedTime() throws Exception {
|
||||||
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times1));
|
setCpusAndData(times1);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
|
|
||||||
// Verify that there should not be a callback for a particular UID if its time decreases.
|
// Verify that there should not be a callback for a particular UID if its time decreases.
|
||||||
@@ -155,19 +172,19 @@ public class KernelCpuUidActiveTimeReaderTest {
|
|||||||
final long[][] times2 = increaseTime(times1);
|
final long[][] times2 = increaseTime(times1);
|
||||||
System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
|
System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
|
||||||
times2[0][0] = 100;
|
times2[0][0] = 100;
|
||||||
writeToFile(mHeadline + uidLines(mUids, times2));
|
setCpusAndData(times2);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 1; i < mUids.length; i++) {
|
for (int i = 1; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
|
mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
|
||||||
}
|
}
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
assertTrue(mTestFile.delete());
|
clearCpusAndData();
|
||||||
|
|
||||||
// Verify that the internal state was not modified.
|
// Verify that the internal state was not modified.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times3 = increaseTime(times2);
|
final long[][] times3 = increaseTime(times2);
|
||||||
times3[0] = increaseTime(times1)[0];
|
times3[0] = increaseTime(times1)[0];
|
||||||
writeToFile(mHeadline + uidLines(mUids, times3));
|
setCpusAndData(times3);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
|
mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
|
||||||
for (int i = 1; i < mUids.length; i++) {
|
for (int i = 1; i < mUids.length; i++) {
|
||||||
@@ -179,26 +196,26 @@ public class KernelCpuUidActiveTimeReaderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testReadDeltaNegativeTime() throws Exception {
|
public void testReadDeltaNegativeTime() throws Exception {
|
||||||
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times1));
|
setCpusAndData(times1);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
|
|
||||||
// Verify that there should not be a callback for a particular UID if its time is -ve.
|
// Verify that there should not be a callback for a particular UID if its time is -ve.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times2 = increaseTime(times1);
|
final long[][] times2 = increaseTime(times1);
|
||||||
times2[0][0] *= -1;
|
times2[0][0] *= -1;
|
||||||
writeToFile(mHeadline + uidLines(mUids, times2));
|
setCpusAndData(times2);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 1; i < mUids.length; i++) {
|
for (int i = 1; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
|
mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
|
||||||
}
|
}
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
assertTrue(mTestFile.delete());
|
clearCpusAndData();
|
||||||
|
|
||||||
// Verify that the internal state was not modified.
|
// Verify that the internal state was not modified.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times3 = increaseTime(times2);
|
final long[][] times3 = increaseTime(times2);
|
||||||
times3[0] = increaseTime(times1)[0];
|
times3[0] = increaseTime(times1)[0];
|
||||||
writeToFile(mHeadline + uidLines(mUids, times3));
|
setCpusAndData(times3);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
|
mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
|
||||||
for (int i = 1; i < mUids.length; i++) {
|
for (int i = 1; i < mUids.length; i++) {
|
||||||
@@ -207,6 +224,28 @@ public class KernelCpuUidActiveTimeReaderTest {
|
|||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setCpusAndData(long[][] times) throws IOException {
|
||||||
|
if (mUseBpf) {
|
||||||
|
mBpfMapReader.setCpus(new long[]{ mCpus });
|
||||||
|
SparseArray<long[]> data = new SparseArray<>();
|
||||||
|
for (int i = 0; i < mUids.length; i++) {
|
||||||
|
data.put(mUids[i], times[i]);
|
||||||
|
}
|
||||||
|
mBpfMapReader.setData(data);
|
||||||
|
} else {
|
||||||
|
writeToFile(mHeadline + uidLines(mUids, times));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearCpusAndData() {
|
||||||
|
if (mUseBpf) {
|
||||||
|
mBpfMapReader.setCpus(null);
|
||||||
|
mBpfMapReader.setData(new SparseArray<>());
|
||||||
|
} else {
|
||||||
|
assertTrue(mTestFile.delete());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String uidLines(int[] uids, long[][] times) {
|
private String uidLines(int[] uids, long[][] times) {
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
for (int i = 0; i < uids.length; i++) {
|
for (int i = 0; i < uids.length; i++) {
|
||||||
@@ -261,4 +300,36 @@ public class KernelCpuUidActiveTimeReaderTest {
|
|||||||
assertEquals(0, mData.size());
|
assertEquals(0, mData.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
|
||||||
|
private long[] mCpus;
|
||||||
|
private SparseArray<long[]> mNewData = new SparseArray<>();
|
||||||
|
|
||||||
|
public void setData(SparseArray<long[]> data) {
|
||||||
|
mNewData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCpus(long[] cpus) {
|
||||||
|
mCpus = cpus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean startTrackingBpfTimes() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final boolean readBpfData() {
|
||||||
|
if (!mUseBpf) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mData = mNewData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final long[] getDataDimensions() {
|
||||||
|
return mCpus;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.internal.os;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
|
import androidx.test.InstrumentationRegistry;
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class KernelCpuUidBpfMapReaderTest {
|
||||||
|
private Random mRand = new Random(12345);
|
||||||
|
private KernelCpuUidTestBpfMapReader mReader;
|
||||||
|
|
||||||
|
private Context getContext() {
|
||||||
|
return InstrumentationRegistry.getContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mReader = new KernelCpuUidTestBpfMapReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that reading returns null if readBpfData() fails.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testUnsuccessfulRead() {
|
||||||
|
assertEquals(null, mReader.open());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that reading will always return null after 5 failures.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testReadErrorsLimit() {
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
try (BpfMapIterator iter = mReader.open()) {
|
||||||
|
assertNull(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SparseArray<long[]> data = new SparseArray<>();
|
||||||
|
long[] times = {2};
|
||||||
|
data.put(1, new long[]{2});
|
||||||
|
mReader.setData(data);
|
||||||
|
testOpenAndReadData(data);
|
||||||
|
|
||||||
|
mReader.setData(null);
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
try (BpfMapIterator iter = mReader.open(true)) {
|
||||||
|
assertNull(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mReader.setData(data);
|
||||||
|
try (BpfMapIterator iter = mReader.open(true)) {
|
||||||
|
assertNull(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests getNextUid functionality. */
|
||||||
|
@Test
|
||||||
|
public void testGetNextUid() {
|
||||||
|
final SparseArray<long[]> data = getTestSparseArray(800, 50);
|
||||||
|
mReader.setData(data);
|
||||||
|
testOpenAndReadData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConcurrent() throws Exception {
|
||||||
|
final SparseArray<long[]> data = getTestSparseArray(200, 50);
|
||||||
|
final SparseArray<long[]> data1 = getTestSparseArray(180, 70);
|
||||||
|
final List<Throwable> errs = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
mReader.setData(data);
|
||||||
|
// An additional thread for modifying the data.
|
||||||
|
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(11);
|
||||||
|
final CountDownLatch ready = new CountDownLatch(10);
|
||||||
|
final CountDownLatch start = new CountDownLatch(1);
|
||||||
|
final CountDownLatch modify = new CountDownLatch(1);
|
||||||
|
final CountDownLatch done = new CountDownLatch(10);
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
threadPool.submit(() -> {
|
||||||
|
ready.countDown();
|
||||||
|
try {
|
||||||
|
start.await();
|
||||||
|
testOpenAndReadData(data);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
errs.add(e);
|
||||||
|
} finally {
|
||||||
|
done.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
threadPool.submit(() -> {
|
||||||
|
ready.countDown();
|
||||||
|
try {
|
||||||
|
start.await();
|
||||||
|
// Wait for data modification.
|
||||||
|
modify.await();
|
||||||
|
testOpenAndReadData(data1);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
errs.add(e);
|
||||||
|
} finally {
|
||||||
|
done.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue("Prep timed out", ready.await(100, TimeUnit.MILLISECONDS));
|
||||||
|
start.countDown();
|
||||||
|
|
||||||
|
threadPool.schedule(() -> {
|
||||||
|
mReader.setData(data1);
|
||||||
|
modify.countDown();
|
||||||
|
}, 600, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
assertTrue("Execution timed out", done.await(3, TimeUnit.SECONDS));
|
||||||
|
threadPool.shutdownNow();
|
||||||
|
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
PrintWriter pw = new PrintWriter(sw);
|
||||||
|
errs.forEach(e -> e.printStackTrace(pw));
|
||||||
|
|
||||||
|
assertTrue("All Exceptions:\n" + sw.toString(), errs.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveUidsInRange() {
|
||||||
|
final SparseArray<long[]> data = getTestSparseArray(200, 50);
|
||||||
|
mReader.setData(data);
|
||||||
|
testOpenAndReadData(data);
|
||||||
|
SparseArray<long[]> changedData = new SparseArray<>();
|
||||||
|
for (int i = 6; i < 200; i++) {
|
||||||
|
changedData.put(i, data.get(i));
|
||||||
|
}
|
||||||
|
mReader.removeUidsInRange(0, 5);
|
||||||
|
testOpenAndReadData(changedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testOpenAndReadData(SparseArray<long[]> expectedData) {
|
||||||
|
try (BpfMapIterator iter = mReader.open()) {
|
||||||
|
long[] actual;
|
||||||
|
if (expectedData.size() > 0) {
|
||||||
|
actual = new long[expectedData.valueAt(0).length + 1];
|
||||||
|
} else {
|
||||||
|
actual = new long[0];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < expectedData.size(); i++) {
|
||||||
|
assertTrue(iter.getNextUid(actual));
|
||||||
|
assertEquals(expectedData.keyAt(i), actual[0]);
|
||||||
|
assertArrayEquals(expectedData.valueAt(i), Arrays.copyOfRange(actual, 1, actual.length));
|
||||||
|
}
|
||||||
|
assertFalse(iter.getNextUid(actual));
|
||||||
|
assertFalse(iter.getNextUid(actual));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private SparseArray<long[]> getTestSparseArray(int uids, int numPerUid) {
|
||||||
|
SparseArray<long[]> data = new SparseArray<>();
|
||||||
|
for (int i = 0; i < uids; i++) {
|
||||||
|
data.put(i, mRand.longs(numPerUid, 0, Long.MAX_VALUE).toArray());
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
|
||||||
|
private SparseArray<long[]> mNewData;
|
||||||
|
|
||||||
|
public final void setData(SparseArray<long[]> newData) {
|
||||||
|
mNewData = newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean startTrackingBpfTimes() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean readBpfData() {
|
||||||
|
if (mNewData == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mData = mNewData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final long[] getDataDimensions() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,6 @@ import android.util.SparseArray;
|
|||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
import androidx.test.InstrumentationRegistry;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
|
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
|
||||||
|
|
||||||
@@ -35,11 +34,15 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,28 +51,42 @@ import java.util.Random;
|
|||||||
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidClusterTimeReaderTest
|
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidClusterTimeReaderTest
|
||||||
*/
|
*/
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(Parameterized.class)
|
||||||
public class KernelCpuUidClusterTimeReaderTest {
|
public class KernelCpuUidClusterTimeReaderTest {
|
||||||
private File mTestDir;
|
private File mTestDir;
|
||||||
private File mTestFile;
|
private File mTestFile;
|
||||||
private KernelCpuUidClusterTimeReader mReader;
|
private KernelCpuUidClusterTimeReader mReader;
|
||||||
|
private KernelCpuUidTestBpfMapReader mBpfMapReader;
|
||||||
private VerifiableCallback mCallback;
|
private VerifiableCallback mCallback;
|
||||||
|
|
||||||
private Random mRand = new Random(12345);
|
private Random mRand = new Random(12345);
|
||||||
|
protected boolean mUseBpf;
|
||||||
private final int mCpus = 6;
|
private final int mCpus = 6;
|
||||||
private final String mHeadline = "policy0: 4 policy4: 2\n";
|
private final String mHeadline = "policy0: 4 policy4: 2\n";
|
||||||
|
private final long[] mCores = {4, 2};
|
||||||
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
|
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
|
||||||
|
|
||||||
private Context getContext() {
|
private Context getContext() {
|
||||||
return InstrumentationRegistry.getContext();
|
return InstrumentationRegistry.getContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parameters(name="useBpf={0}")
|
||||||
|
public static Collection<Object[]> data() {
|
||||||
|
return Arrays.asList(new Object[][] { {true}, {false} });
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelCpuUidClusterTimeReaderTest(boolean useBpf) {
|
||||||
|
mUseBpf = useBpf;
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
|
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
|
||||||
mTestFile = new File(mTestDir, "test.file");
|
mTestFile = new File(mTestDir, "test.file");
|
||||||
|
mBpfMapReader = new KernelCpuUidTestBpfMapReader();
|
||||||
mReader = new KernelCpuUidClusterTimeReader(
|
mReader = new KernelCpuUidClusterTimeReader(
|
||||||
new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
|
new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader,
|
||||||
|
false);
|
||||||
mCallback = new VerifiableCallback();
|
mCallback = new VerifiableCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +99,7 @@ public class KernelCpuUidClusterTimeReaderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testReadDelta() throws Exception {
|
public void testReadDelta() throws Exception {
|
||||||
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times1));
|
setCoresAndData(times1);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 0; i < mUids.length; ++i) {
|
for (int i = 0; i < mUids.length; ++i) {
|
||||||
mCallback.verify(mUids[i], clusterTime(times1[i]));
|
mCallback.verify(mUids[i], clusterTime(times1[i]));
|
||||||
@@ -92,7 +109,7 @@ public class KernelCpuUidClusterTimeReaderTest {
|
|||||||
// Verify that a second call will only return deltas.
|
// Verify that a second call will only return deltas.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times2 = increaseTime(times1);
|
final long[][] times2 = increaseTime(times1);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times2));
|
setCoresAndData(times2);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 0; i < mUids.length; ++i) {
|
for (int i = 0; i < mUids.length; ++i) {
|
||||||
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
|
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
|
||||||
@@ -107,7 +124,7 @@ public class KernelCpuUidClusterTimeReaderTest {
|
|||||||
// Verify that calling with a null callback doesn't result in any crashes
|
// Verify that calling with a null callback doesn't result in any crashes
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times3 = increaseTime(times2);
|
final long[][] times3 = increaseTime(times2);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times3));
|
setCoresAndData(times3);
|
||||||
mReader.readDelta(null);
|
mReader.readDelta(null);
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
|
|
||||||
@@ -115,19 +132,19 @@ public class KernelCpuUidClusterTimeReaderTest {
|
|||||||
// the previous call had null callback.
|
// the previous call had null callback.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times4 = increaseTime(times3);
|
final long[][] times4 = increaseTime(times3);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times4));
|
setCoresAndData(times4);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 0; i < mUids.length; ++i) {
|
for (int i = 0; i < mUids.length; ++i) {
|
||||||
mCallback.verify(mUids[i], subtract(clusterTime(times4[i]), clusterTime(times3[i])));
|
mCallback.verify(mUids[i], subtract(clusterTime(times4[i]), clusterTime(times3[i])));
|
||||||
}
|
}
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
assertTrue(mTestFile.delete());
|
clearCoresAndData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadAbsolute() throws Exception {
|
public void testReadAbsolute() throws Exception {
|
||||||
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times1));
|
setCoresAndData(times1);
|
||||||
mReader.readAbsolute(mCallback);
|
mReader.readAbsolute(mCallback);
|
||||||
for (int i = 0; i < mUids.length; i++) {
|
for (int i = 0; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], clusterTime(times1[i]));
|
mCallback.verify(mUids[i], clusterTime(times1[i]));
|
||||||
@@ -137,19 +154,19 @@ public class KernelCpuUidClusterTimeReaderTest {
|
|||||||
// Verify that a second call should still return absolute values
|
// Verify that a second call should still return absolute values
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times2 = increaseTime(times1);
|
final long[][] times2 = increaseTime(times1);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times2));
|
setCoresAndData(times2);
|
||||||
mReader.readAbsolute(mCallback);
|
mReader.readAbsolute(mCallback);
|
||||||
for (int i = 0; i < mUids.length; i++) {
|
for (int i = 0; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], clusterTime(times2[i]));
|
mCallback.verify(mUids[i], clusterTime(times2[i]));
|
||||||
}
|
}
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
assertTrue(mTestFile.delete());
|
clearCoresAndData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadDeltaDecreasedTime() throws Exception {
|
public void testReadDeltaDecreasedTime() throws Exception {
|
||||||
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times1));
|
setCoresAndData(times1);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
|
|
||||||
// Verify that there should not be a callback for a particular UID if its time decreases.
|
// Verify that there should not be a callback for a particular UID if its time decreases.
|
||||||
@@ -157,19 +174,19 @@ public class KernelCpuUidClusterTimeReaderTest {
|
|||||||
final long[][] times2 = increaseTime(times1);
|
final long[][] times2 = increaseTime(times1);
|
||||||
System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
|
System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
|
||||||
times2[0][0] = 100;
|
times2[0][0] = 100;
|
||||||
writeToFile(mHeadline + uidLines(mUids, times2));
|
setCoresAndData(times2);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 1; i < mUids.length; i++) {
|
for (int i = 1; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
|
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
|
||||||
}
|
}
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
assertTrue(mTestFile.delete());
|
clearCoresAndData();
|
||||||
|
|
||||||
// Verify that the internal state was not modified.
|
// Verify that the internal state was not modified.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times3 = increaseTime(times2);
|
final long[][] times3 = increaseTime(times2);
|
||||||
times3[0] = increaseTime(times1)[0];
|
times3[0] = increaseTime(times1)[0];
|
||||||
writeToFile(mHeadline + uidLines(mUids, times3));
|
setCoresAndData(times3);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
|
mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
|
||||||
for (int i = 1; i < mUids.length; i++) {
|
for (int i = 1; i < mUids.length; i++) {
|
||||||
@@ -181,26 +198,26 @@ public class KernelCpuUidClusterTimeReaderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testReadDeltaNegativeTime() throws Exception {
|
public void testReadDeltaNegativeTime() throws Exception {
|
||||||
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
|
||||||
writeToFile(mHeadline + uidLines(mUids, times1));
|
setCoresAndData(times1);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
|
|
||||||
// Verify that there should not be a callback for a particular UID if its time decreases.
|
// Verify that there should not be a callback for a particular UID if its time decreases.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times2 = increaseTime(times1);
|
final long[][] times2 = increaseTime(times1);
|
||||||
times2[0][0] *= -1;
|
times2[0][0] *= -1;
|
||||||
writeToFile(mHeadline + uidLines(mUids, times2));
|
setCoresAndData(times2);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 1; i < mUids.length; i++) {
|
for (int i = 1; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
|
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
|
||||||
}
|
}
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
assertTrue(mTestFile.delete());
|
clearCoresAndData();
|
||||||
|
|
||||||
// Verify that the internal state was not modified.
|
// Verify that the internal state was not modified.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times3 = increaseTime(times2);
|
final long[][] times3 = increaseTime(times2);
|
||||||
times3[0] = increaseTime(times1)[0];
|
times3[0] = increaseTime(times1)[0];
|
||||||
writeToFile(mHeadline + uidLines(mUids, times3));
|
setCoresAndData(times3);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
|
mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
|
||||||
for (int i = 1; i < mUids.length; i++) {
|
for (int i = 1; i < mUids.length; i++) {
|
||||||
@@ -209,6 +226,28 @@ public class KernelCpuUidClusterTimeReaderTest {
|
|||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setCoresAndData(long[][] times) throws IOException {
|
||||||
|
if (mUseBpf) {
|
||||||
|
mBpfMapReader.setClusterCores(mCores);
|
||||||
|
SparseArray<long[]> data = new SparseArray<>();
|
||||||
|
for (int i = 0; i < mUids.length; i++) {
|
||||||
|
data.put(mUids[i], times[i]);
|
||||||
|
}
|
||||||
|
mBpfMapReader.setData(data);
|
||||||
|
} else {
|
||||||
|
writeToFile(mHeadline + uidLines(mUids, times));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearCoresAndData() {
|
||||||
|
if (mUseBpf) {
|
||||||
|
mBpfMapReader.setClusterCores(null);
|
||||||
|
mBpfMapReader.setData(new SparseArray<>());
|
||||||
|
} else {
|
||||||
|
assertTrue(mTestFile.delete());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private long[] clusterTime(long[] times) {
|
private long[] clusterTime(long[] times) {
|
||||||
// Assumes 4 + 2 cores
|
// Assumes 4 + 2 cores
|
||||||
return new long[]{times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4,
|
return new long[]{times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4,
|
||||||
@@ -277,4 +316,36 @@ public class KernelCpuUidClusterTimeReaderTest {
|
|||||||
assertEquals(0, mData.size());
|
assertEquals(0, mData.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
|
||||||
|
private long[] mClusterCores;
|
||||||
|
private SparseArray<long[]> mNewData = new SparseArray<>();
|
||||||
|
|
||||||
|
public void setData(SparseArray<long[]> data) {
|
||||||
|
mNewData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClusterCores(long[] cores) {
|
||||||
|
mClusterCores = cores;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean startTrackingBpfTimes() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final boolean readBpfData() {
|
||||||
|
if (!mUseBpf) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mData = mNewData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final long[] getDataDimensions() {
|
||||||
|
return mClusterCores;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import android.util.SparseArray;
|
|||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
import androidx.test.InstrumentationRegistry;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
|
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
|
||||||
|
|
||||||
@@ -37,6 +36,8 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,14 +55,16 @@ import java.util.Random;
|
|||||||
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidFreqTimeReaderTest
|
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidFreqTimeReaderTest
|
||||||
*/
|
*/
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(Parameterized.class)
|
||||||
public class KernelCpuUidFreqTimeReaderTest {
|
public class KernelCpuUidFreqTimeReaderTest {
|
||||||
private File mTestDir;
|
private File mTestDir;
|
||||||
private File mTestFile;
|
private File mTestFile;
|
||||||
private KernelCpuUidFreqTimeReader mReader;
|
private KernelCpuUidFreqTimeReader mReader;
|
||||||
|
private KernelCpuUidTestBpfMapReader mBpfMapReader;
|
||||||
private VerifiableCallback mCallback;
|
private VerifiableCallback mCallback;
|
||||||
@Mock
|
@Mock
|
||||||
private PowerProfile mPowerProfile;
|
private PowerProfile mPowerProfile;
|
||||||
|
private boolean mUseBpf;
|
||||||
|
|
||||||
private Random mRand = new Random(12345);
|
private Random mRand = new Random(12345);
|
||||||
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
|
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
|
||||||
@@ -69,13 +73,23 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
return InstrumentationRegistry.getContext();
|
return InstrumentationRegistry.getContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parameters(name="useBpf={0}")
|
||||||
|
public static Collection<Object[]> data() {
|
||||||
|
return Arrays.asList(new Object[][] { {true}, {false} });
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelCpuUidFreqTimeReaderTest(boolean useBpf) {
|
||||||
|
mUseBpf = useBpf;
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
|
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
|
||||||
mTestFile = new File(mTestDir, "test.file");
|
mTestFile = new File(mTestDir, "test.file");
|
||||||
|
mBpfMapReader = new KernelCpuUidTestBpfMapReader();
|
||||||
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
|
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
|
||||||
new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
|
new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
|
||||||
mCallback = new VerifiableCallback();
|
mCallback = new VerifiableCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,17 +111,17 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
|
final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
|
||||||
for (int i = 0; i < freqs.length; ++i) {
|
for (int i = 0; i < freqs.length; ++i) {
|
||||||
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
|
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
|
||||||
new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
|
new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
|
||||||
setCpuClusterFreqs(numClusters[i], numFreqs[i]);
|
setCpuClusterFreqs(numClusters[i], numFreqs[i]);
|
||||||
writeToFile(freqsLine(freqs[i]));
|
setFreqs(freqs[i]);
|
||||||
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
|
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
|
||||||
assertArrayEquals(freqs[i], actualFreqs);
|
assertArrayEquals(freqs[i], actualFreqs);
|
||||||
final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
|
final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
|
||||||
Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
|
Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
|
||||||
assertFalse(errMsg, mReader.perClusterTimesAvailable());
|
assertFalse(errMsg, mReader.perClusterTimesAvailable());
|
||||||
|
|
||||||
// Verify that a second call won't read the proc file again
|
// Verify that a second call won't re-read the freqs
|
||||||
assertTrue(mTestFile.delete());
|
clearFreqsAndData();
|
||||||
actualFreqs = mReader.readFreqs(mPowerProfile);
|
actualFreqs = mReader.readFreqs(mPowerProfile);
|
||||||
assertArrayEquals(freqs[i], actualFreqs);
|
assertArrayEquals(freqs[i], actualFreqs);
|
||||||
assertFalse(errMsg, mReader.perClusterTimesAvailable());
|
assertFalse(errMsg, mReader.perClusterTimesAvailable());
|
||||||
@@ -125,17 +139,17 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
|
final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
|
||||||
for (int i = 0; i < freqs.length; ++i) {
|
for (int i = 0; i < freqs.length; ++i) {
|
||||||
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
|
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
|
||||||
new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
|
new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
|
||||||
setCpuClusterFreqs(numClusters[i], numFreqs[i]);
|
setCpuClusterFreqs(numClusters[i], numFreqs[i]);
|
||||||
writeToFile(freqsLine(freqs[i]));
|
setFreqs(freqs[i]);
|
||||||
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
|
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
|
||||||
assertArrayEquals(freqs[i], actualFreqs);
|
assertArrayEquals(freqs[i], actualFreqs);
|
||||||
final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
|
final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
|
||||||
Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
|
Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
|
||||||
assertTrue(errMsg, mReader.perClusterTimesAvailable());
|
assertTrue(errMsg, mReader.perClusterTimesAvailable());
|
||||||
|
|
||||||
// Verify that a second call won't read the proc file again
|
// Verify that a second call won't re-read the freqs
|
||||||
assertTrue(mTestFile.delete());
|
clearFreqsAndData();
|
||||||
actualFreqs = mReader.readFreqs(mPowerProfile);
|
actualFreqs = mReader.readFreqs(mPowerProfile);
|
||||||
assertArrayEquals(freqs[i], actualFreqs);
|
assertArrayEquals(freqs[i], actualFreqs);
|
||||||
assertTrue(errMsg, mReader.perClusterTimesAvailable());
|
assertTrue(errMsg, mReader.perClusterTimesAvailable());
|
||||||
@@ -147,7 +161,7 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
final long[] freqs = {110, 123, 145, 167, 289, 997};
|
final long[] freqs = {110, 123, 145, 167, 289, 997};
|
||||||
final long[][] times = increaseTime(new long[mUids.length][freqs.length]);
|
final long[][] times = increaseTime(new long[mUids.length][freqs.length]);
|
||||||
|
|
||||||
writeToFile(freqsLine(freqs) + uidLines(mUids, times));
|
setFreqsAndData(freqs, times);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 0; i < mUids.length; ++i) {
|
for (int i = 0; i < mUids.length; ++i) {
|
||||||
mCallback.verify(mUids[i], times[i]);
|
mCallback.verify(mUids[i], times[i]);
|
||||||
@@ -155,14 +169,14 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
|
|
||||||
// Verify that readDelta also reads the frequencies if not already available.
|
// Verify that readDelta also reads the frequencies if not already available.
|
||||||
assertTrue(mTestFile.delete());
|
clearFreqsAndData();
|
||||||
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
|
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
|
||||||
assertArrayEquals(freqs, actualFreqs);
|
assertArrayEquals(freqs, actualFreqs);
|
||||||
|
|
||||||
// Verify that a second call will only return deltas.
|
// Verify that a second call will only return deltas.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] newTimes1 = increaseTime(times);
|
final long[][] newTimes1 = increaseTime(times);
|
||||||
writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes1));
|
setFreqsAndData(freqs, newTimes1);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 0; i < mUids.length; ++i) {
|
for (int i = 0; i < mUids.length; ++i) {
|
||||||
mCallback.verify(mUids[i], subtract(newTimes1[i], times[i]));
|
mCallback.verify(mUids[i], subtract(newTimes1[i], times[i]));
|
||||||
@@ -177,7 +191,7 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
// Verify that calling with a null callback doesn't result in any crashes
|
// Verify that calling with a null callback doesn't result in any crashes
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] newTimes2 = increaseTime(newTimes1);
|
final long[][] newTimes2 = increaseTime(newTimes1);
|
||||||
writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes2));
|
setFreqsAndData(freqs, newTimes2);
|
||||||
mReader.readDelta(null);
|
mReader.readDelta(null);
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
|
|
||||||
@@ -185,13 +199,13 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
// the previous call had null callback.
|
// the previous call had null callback.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] newTimes3 = increaseTime(newTimes2);
|
final long[][] newTimes3 = increaseTime(newTimes2);
|
||||||
writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes3));
|
setFreqsAndData(freqs, newTimes3);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 0; i < mUids.length; ++i) {
|
for (int i = 0; i < mUids.length; ++i) {
|
||||||
mCallback.verify(mUids[i], subtract(newTimes3[i], newTimes2[i]));
|
mCallback.verify(mUids[i], subtract(newTimes3[i], newTimes2[i]));
|
||||||
}
|
}
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
assertTrue(mTestFile.delete());
|
clearFreqsAndData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -199,7 +213,7 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
final long[] freqs = {110, 123, 145, 167, 289, 997};
|
final long[] freqs = {110, 123, 145, 167, 289, 997};
|
||||||
final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
|
final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
|
||||||
|
|
||||||
writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
|
setFreqsAndData(freqs, times1);
|
||||||
mReader.readAbsolute(mCallback);
|
mReader.readAbsolute(mCallback);
|
||||||
for (int i = 0; i < mUids.length; i++) {
|
for (int i = 0; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], times1[i]);
|
mCallback.verify(mUids[i], times1[i]);
|
||||||
@@ -207,20 +221,20 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
|
|
||||||
// Verify that readDelta also reads the frequencies if not already available.
|
// Verify that readDelta also reads the frequencies if not already available.
|
||||||
assertTrue(mTestFile.delete());
|
clearFreqsAndData();
|
||||||
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
|
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
|
||||||
assertArrayEquals(freqs, actualFreqs);
|
assertArrayEquals(freqs, actualFreqs);
|
||||||
|
|
||||||
// Verify that a second call should still return absolute values
|
// Verify that a second call should still return absolute values
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times2 = increaseTime(times1);
|
final long[][] times2 = increaseTime(times1);
|
||||||
writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
|
setFreqsAndData(freqs, times2);
|
||||||
mReader.readAbsolute(mCallback);
|
mReader.readAbsolute(mCallback);
|
||||||
for (int i = 0; i < mUids.length; i++) {
|
for (int i = 0; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], times2[i]);
|
mCallback.verify(mUids[i], times2[i]);
|
||||||
}
|
}
|
||||||
mCallback.verifyNoMoreInteractions();
|
mCallback.verifyNoMoreInteractions();
|
||||||
assertTrue(mTestFile.delete());
|
clearFreqsAndData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -228,14 +242,14 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
final long[] freqs = {110, 123, 145, 167, 289, 997};
|
final long[] freqs = {110, 123, 145, 167, 289, 997};
|
||||||
final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
|
final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
|
||||||
|
|
||||||
writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
|
setFreqsAndData(freqs, times1);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
|
|
||||||
// Verify that there should not be a callback for a particular UID if its time decreases.
|
// Verify that there should not be a callback for a particular UID if its time decreases.
|
||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times2 = increaseTime(times1);
|
final long[][] times2 = increaseTime(times1);
|
||||||
times2[0][0] = 1000;
|
times2[0][0] = 1000;
|
||||||
writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
|
setFreqsAndData(freqs, times2);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 1; i < mUids.length; i++) {
|
for (int i = 1; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
|
mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
|
||||||
@@ -246,7 +260,7 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times3 = increaseTime(times2);
|
final long[][] times3 = increaseTime(times2);
|
||||||
times3[0] = increaseTime(times1)[0];
|
times3[0] = increaseTime(times1)[0];
|
||||||
writeToFile(freqsLine(freqs) + uidLines(mUids, times3));
|
setFreqsAndData(freqs, times3);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
mCallback.verify(mUids[0], subtract(times3[0], times1[0]));
|
mCallback.verify(mUids[0], subtract(times3[0], times1[0]));
|
||||||
for (int i = 1; i < mUids.length; i++) {
|
for (int i = 1; i < mUids.length; i++) {
|
||||||
@@ -258,7 +272,7 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times4 = increaseTime(times3);
|
final long[][] times4 = increaseTime(times3);
|
||||||
times4[0][0] *= -1;
|
times4[0][0] *= -1;
|
||||||
writeToFile(freqsLine(freqs) + uidLines(mUids, times4));
|
setFreqsAndData(freqs, times4);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
for (int i = 1; i < mUids.length; ++i) {
|
for (int i = 1; i < mUids.length; ++i) {
|
||||||
mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
|
mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
|
||||||
@@ -269,14 +283,44 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
mCallback.clear();
|
mCallback.clear();
|
||||||
final long[][] times5 = increaseTime(times4);
|
final long[][] times5 = increaseTime(times4);
|
||||||
times5[0] = increaseTime(times3)[0];
|
times5[0] = increaseTime(times3)[0];
|
||||||
writeToFile(freqsLine(freqs) + uidLines(mUids, times5));
|
setFreqsAndData(freqs, times5);
|
||||||
mReader.readDelta(mCallback);
|
mReader.readDelta(mCallback);
|
||||||
mCallback.verify(mUids[0], subtract(times5[0], times3[0]));
|
mCallback.verify(mUids[0], subtract(times5[0], times3[0]));
|
||||||
for (int i = 1; i < mUids.length; i++) {
|
for (int i = 1; i < mUids.length; i++) {
|
||||||
mCallback.verify(mUids[i], subtract(times5[i], times4[i]));
|
mCallback.verify(mUids[i], subtract(times5[i], times4[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(mTestFile.delete());
|
clearFreqsAndData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFreqs(long[] freqs) throws IOException {
|
||||||
|
if (mUseBpf) {
|
||||||
|
mBpfMapReader.setFreqs(freqs);
|
||||||
|
} else {
|
||||||
|
writeToFile(freqsLine(freqs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFreqsAndData(long[] freqs, long[][] times) throws IOException {
|
||||||
|
if (mUseBpf) {
|
||||||
|
mBpfMapReader.setFreqs(freqs);
|
||||||
|
SparseArray<long[]> data = new SparseArray<>();
|
||||||
|
for (int i = 0; i < mUids.length; i++) {
|
||||||
|
data.put(mUids[i], times[i]);
|
||||||
|
}
|
||||||
|
mBpfMapReader.setData(data);
|
||||||
|
} else {
|
||||||
|
writeToFile(freqsLine(freqs) + uidLines(mUids, times));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearFreqsAndData() {
|
||||||
|
if (mUseBpf) {
|
||||||
|
mBpfMapReader.setFreqs(null);
|
||||||
|
mBpfMapReader.setData(new SparseArray<>());
|
||||||
|
} else {
|
||||||
|
assertTrue(mTestFile.delete());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String freqsLine(long[] freqs) {
|
private String freqsLine(long[] freqs) {
|
||||||
@@ -358,4 +402,36 @@ public class KernelCpuUidFreqTimeReaderTest {
|
|||||||
assertEquals(0, mData.size());
|
assertEquals(0, mData.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
|
||||||
|
private long[] mCpuFreqs;
|
||||||
|
private SparseArray<long[]> mNewData = new SparseArray<>();
|
||||||
|
|
||||||
|
public void setData(SparseArray<long[]> data) {
|
||||||
|
mNewData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFreqs(long[] freqs) {
|
||||||
|
mCpuFreqs = freqs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean startTrackingBpfTimes() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final boolean readBpfData() {
|
||||||
|
if (!mUseBpf) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mData = mNewData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final long[] getDataDimensions() {
|
||||||
|
return mCpuFreqs;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user