Merge "Integrate LongArrayMultiStateCounter with eBPF based time-in-state"
This commit is contained in:
committed by
Android (Google) Code Review
commit
07589854d1
@@ -24,6 +24,8 @@ import android.util.SparseArray;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import dalvik.annotation.optimization.CriticalNative;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
@@ -248,6 +250,32 @@ public class KernelSingleUidTimeReader {
|
||||
}
|
||||
|
||||
public native long[] readBpfData(int uid);
|
||||
|
||||
/**
|
||||
* Reads CPU time-in-state data for the specified UID and adds the delta since the
|
||||
* previous call to the current state stats in the LongArrayMultiStateCounter.
|
||||
*/
|
||||
public boolean addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs) {
|
||||
return addDeltaFromBpf(uid, counter.mNativeObject, timestampMs);
|
||||
}
|
||||
|
||||
@CriticalNative
|
||||
private static native boolean addDeltaFromBpf(int uid,
|
||||
long longArrayMultiStateCounterNativePointer, long timestampMs);
|
||||
|
||||
/**
|
||||
* Used for testing.
|
||||
*
|
||||
* Takes mock cpu-time-in-frequency data and uses it the same way eBPF data would be used.
|
||||
*/
|
||||
public boolean addDeltaForTest(int uid, LongArrayMultiStateCounter counter,
|
||||
long timestampMs, long[][] timeInFreqDataNanos) {
|
||||
return addDeltaForTest(uid, counter.mNativeObject, timestampMs, timeInFreqDataNanos);
|
||||
}
|
||||
|
||||
private static native boolean addDeltaForTest(int uid,
|
||||
long longArrayMultiStateCounterNativePointer, long timestampMs,
|
||||
long[][] timeInFreqDataNanos);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -31,7 +31,7 @@ import libcore.util.NativeAllocationRegistry;
|
||||
* // At 1000 ms, the state changes to 1
|
||||
* counter.setState(1, 1000);
|
||||
*
|
||||
* // At 3000 ms, the tracked values are updated to {100, 200}
|
||||
* // At 3000 ms, the tracked values are updated to {30, 300}
|
||||
* arrayContainer.setValues(new long[]{{30, 300}};
|
||||
* counter.updateValues(arrayContainer, 3000);
|
||||
*
|
||||
@@ -107,7 +107,10 @@ public class LongArrayMultiStateCounter {
|
||||
|
||||
private final int mStateCount;
|
||||
private final int mLength;
|
||||
private final long mNativeObject;
|
||||
|
||||
// Visible to other objects in this package so that it can be passed to @CriticalNative
|
||||
// methods.
|
||||
final long mNativeObject;
|
||||
|
||||
public LongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState,
|
||||
long timestampMs) {
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "core_jni_helpers.h"
|
||||
|
||||
#include <cputimeinstate.h>
|
||||
#include <nativehelper/ScopedPrimitiveArray.h>
|
||||
|
||||
#include "LongArrayMultiStateCounter.h"
|
||||
#include "core_jni_helpers.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -42,8 +44,70 @@ static jlongArray getUidCpuFreqTimeMs(JNIEnv *env, jclass, jint uid) {
|
||||
return copyVecsToArray(env, out.value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes delta of CPU time-in-freq from the previously supplied counts and adds the delta
|
||||
* to the supplied multi-state counter in accordance with the counter's state.
|
||||
*/
|
||||
static jboolean addCpuTimeInFreqDelta(
|
||||
jint uid, jlong counterNativePtr, jlong timestampMs,
|
||||
std::optional<std::vector<std::vector<uint64_t>>> timeInFreqDataNanos) {
|
||||
if (!timeInFreqDataNanos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
battery::LongArrayMultiStateCounter *counter =
|
||||
reinterpret_cast<battery::LongArrayMultiStateCounter *>(counterNativePtr);
|
||||
size_t s = 0;
|
||||
for (const auto &cluster : *timeInFreqDataNanos) s += cluster.size();
|
||||
|
||||
std::vector<uint64_t> flattened;
|
||||
flattened.reserve(s);
|
||||
auto offset = flattened.begin();
|
||||
for (const auto &cluster : *timeInFreqDataNanos) {
|
||||
flattened.insert(offset, cluster.begin(), cluster.end());
|
||||
offset += cluster.size();
|
||||
}
|
||||
for (size_t i = 0; i < s; ++i) {
|
||||
flattened[i] /= NSEC_PER_MSEC;
|
||||
}
|
||||
counter->updateValue(flattened, timestampMs);
|
||||
return true;
|
||||
}
|
||||
|
||||
static jboolean addDeltaFromBpf(jint uid, jlong counterNativePtr, jlong timestampMs) {
|
||||
return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs,
|
||||
android::bpf::getUidCpuFreqTimes(uid));
|
||||
}
|
||||
|
||||
static jboolean addDeltaForTest(JNIEnv *env, jclass, jint uid, jlong counterNativePtr,
|
||||
jlong timestampMs, jobjectArray timeInFreqDataNanos) {
|
||||
if (!timeInFreqDataNanos) {
|
||||
return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs,
|
||||
std::optional<std::vector<std::vector<uint64_t>>>());
|
||||
}
|
||||
|
||||
std::vector<std::vector<uint64_t>> timeInFreqData;
|
||||
jsize len = env->GetArrayLength(timeInFreqDataNanos);
|
||||
for (jsize i = 0; i < len; i++) {
|
||||
std::vector<uint64_t> cluster;
|
||||
ScopedLongArrayRO row(env, (jlongArray)env->GetObjectArrayElement(timeInFreqDataNanos, i));
|
||||
cluster.reserve(row.size());
|
||||
for (size_t j = 0; j < row.size(); j++) {
|
||||
cluster.push_back(row[j]);
|
||||
}
|
||||
timeInFreqData.push_back(cluster);
|
||||
}
|
||||
return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs, std::optional(timeInFreqData));
|
||||
}
|
||||
|
||||
static const JNINativeMethod g_single_methods[] = {
|
||||
{"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs},
|
||||
{"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs},
|
||||
|
||||
// @CriticalNative
|
||||
{"addDeltaFromBpf", "(IJJ)Z", (void *)addDeltaFromBpf},
|
||||
|
||||
// Used for testing
|
||||
{"addDeltaForTest", "(IJJ[[J)Z", (void *)addDeltaForTest},
|
||||
};
|
||||
|
||||
int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.internal.os;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -24,7 +26,6 @@ import static org.junit.Assert.assertTrue;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.os.KernelSingleUidTimeReader.Injector;
|
||||
|
||||
@@ -279,6 +280,46 @@ public class KernelSingleUidTimeReaderTest {
|
||||
0, lastUidCpuTimes.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddDeltaFromBpf() {
|
||||
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 5, 0, 0);
|
||||
|
||||
// Nanoseconds
|
||||
mInjector.setCpuTimeInStatePerClusterNs(
|
||||
new long[][]{
|
||||
{1_000_000, 2_000_000, 3_000_000},
|
||||
{4_000_000, 5_000_000}});
|
||||
|
||||
boolean success = mInjector.addDelta(TEST_UID, counter, 2000);
|
||||
assertThat(success).isTrue();
|
||||
|
||||
LongArrayMultiStateCounter.LongArrayContainer array =
|
||||
new LongArrayMultiStateCounter.LongArrayContainer(5);
|
||||
long[] out = new long[5];
|
||||
|
||||
counter.getCounts(array, 0);
|
||||
array.getValues(out);
|
||||
assertThat(out).isEqualTo(new long[]{1, 2, 3, 4, 5});
|
||||
|
||||
counter.setState(1, 3000);
|
||||
|
||||
mInjector.setCpuTimeInStatePerClusterNs(
|
||||
new long[][]{
|
||||
{11_000_000, 22_000_000, 33_000_000},
|
||||
{44_000_000, 55_000_000}});
|
||||
|
||||
success = mInjector.addDelta(TEST_UID, counter, 4000);
|
||||
assertThat(success).isTrue();
|
||||
|
||||
counter.getCounts(array, 0);
|
||||
array.getValues(out);
|
||||
assertThat(out).isEqualTo(new long[]{1 + 5, 2 + 10, 3 + 15, 4 + 20, 5 + 25});
|
||||
|
||||
counter.getCounts(array, 1);
|
||||
array.getValues(out);
|
||||
assertThat(out).isEqualTo(new long[]{5, 10, 15, 20, 25});
|
||||
}
|
||||
|
||||
private void assertCpuTimesEqual(long[] expected, long[] actual) {
|
||||
assertArrayEquals("Expected=" + Arrays.toString(expected)
|
||||
+ ", Actual=" + Arrays.toString(actual), expected, actual);
|
||||
@@ -288,6 +329,7 @@ public class KernelSingleUidTimeReaderTest {
|
||||
private byte[] mData;
|
||||
private long[] mBpfData;
|
||||
private boolean mThrowExcpetion;
|
||||
private long[][] mCpuTimeInStatePerClusterNs;
|
||||
|
||||
@Override
|
||||
public byte[] readData(String procFile) throws IOException {
|
||||
@@ -316,8 +358,17 @@ public class KernelSingleUidTimeReaderTest {
|
||||
mBpfData = cpuTimes.clone();
|
||||
}
|
||||
|
||||
public void setCpuTimeInStatePerClusterNs(long[][] cpuTimeInStatePerClusterNs) {
|
||||
mCpuTimeInStatePerClusterNs = cpuTimeInStatePerClusterNs;
|
||||
}
|
||||
|
||||
public void letReadDataThrowException(boolean throwException) {
|
||||
mThrowExcpetion = throwException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs) {
|
||||
return addDeltaForTest(uid, counter, timestampMs, mCpuTimeInStatePerClusterNs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user