Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 07589854 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Integrate LongArrayMultiStateCounter with eBPF based time-in-state"

parents 13666700 abe852ba
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -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
+5 −2
Original line number Diff line number Diff line
@@ -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) {
+67 −3
Original line number Diff line number Diff line
@@ -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},

        // @CriticalNative
        {"addDeltaFromBpf", "(IJJ)Z", (void *)addDeltaFromBpf},

        // Used for testing
        {"addDeltaForTest", "(IJJ[[J)Z", (void *)addDeltaForTest},
};

int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) {
+52 −1
Original line number Diff line number Diff line
@@ -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);
        }
    }
}