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

Commit 88c3309b authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Eliminate LongArrayContainer

Bug: 315052795
Bug: 357697495
Test: atest libbattery_test; atest FrameworksCoreTests; atest PowerStatsTests
Flag: EXEMPT bugfix

Change-Id: I283d9f9eb81d1d23b230f47e9a02c891a1c66203
parent d7e89209
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -159,9 +159,7 @@ public class LongArrayMultiStateCounterPerfTest {
        LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        long time = 1000;
        LongArrayMultiStateCounter.LongArrayContainer timeInFreq =
                new LongArrayMultiStateCounter.LongArrayContainer(4);
        timeInFreq.setValues(new long[]{100, 200, 300, 400});
        long[] timeInFreq = {100, 200, 300, 400};
        while (state.keepRunning()) {
            counter.setState(1, time);
            counter.setState(0, time + 1000);
+1 −1
Original line number Diff line number Diff line
@@ -1315,7 +1315,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
        }

        synchronized (BatteryUsageStats.class) {
            if (!sInstances.isEmpty()) {
            if (sInstances != null && !sInstances.isEmpty()) {
                Exception callSite = sInstances.entrySet().iterator().next().getValue();
                int count = sInstances.size();
                sInstances.clear();
+9 −12
Original line number Diff line number Diff line
@@ -18,14 +18,13 @@ package com.android.internal.os;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Slog;
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;
@@ -255,8 +254,8 @@ public class KernelSingleUidTimeReader {
     * the delta in the supplied array container.
     */
    public void addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs,
            LongArrayMultiStateCounter.LongArrayContainer deltaContainer) {
        mInjector.addDelta(uid, counter, timestampMs, deltaContainer);
            long[] delta) {
        mInjector.addDelta(uid, counter, timestampMs, delta);
    }

    @VisibleForTesting
@@ -274,15 +273,13 @@ public class KernelSingleUidTimeReader {
         * The delta is also returned via the optional deltaOut parameter.
         */
        public boolean addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs,
                LongArrayMultiStateCounter.LongArrayContainer deltaOut) {
            return addDeltaFromBpf(uid, counter.mNativeObject, timestampMs,
                    deltaOut != null ? deltaOut.mNativeObject : 0);
                long[] deltaOut) {
            return addDeltaFromBpf(uid, counter.mNativeObject, timestampMs, deltaOut);
        }

        @CriticalNative
        private static native boolean addDeltaFromBpf(int uid,
                long longArrayMultiStateCounterNativePointer, long timestampMs,
                long longArrayContainerNativePointer);
                @Nullable long[] deltaOut);

        /**
         * Used for testing.
@@ -291,14 +288,14 @@ public class KernelSingleUidTimeReader {
         */
        public boolean addDeltaForTest(int uid, LongArrayMultiStateCounter counter,
                long timestampMs, long[][] timeInFreqDataNanos,
                LongArrayMultiStateCounter.LongArrayContainer deltaOut) {
                long[] deltaOut) {
            return addDeltaForTest(uid, counter.mNativeObject, timestampMs, timeInFreqDataNanos,
                    deltaOut != null ? deltaOut.mNativeObject : 0);
                    deltaOut);
        }

        private static native boolean addDeltaForTest(int uid,
                long longArrayMultiStateCounterNativePointer, long timestampMs,
                long[][] timeInFreqDataNanos, long longArrayContainerNativePointer);
                long[][] timeInFreqDataNanos, long[] deltaOut);
    }

    @VisibleForTesting
+40 −193
Original line number Diff line number Diff line
@@ -30,9 +30,6 @@ import dalvik.annotation.optimization.FastNative;

import libcore.util.NativeAllocationRegistry;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Performs per-state counting of multi-element values over time. The class' behavior is illustrated
 * by this example:
@@ -44,15 +41,14 @@ import java.util.concurrent.atomic.AtomicReference;
 *   counter.setState(1, 1000);
 *
 *   // At 3000 ms, the tracked values are updated to {30, 300}
 *   arrayContainer.setValues(new long[]{{30, 300}};
 *   counter.updateValues(arrayContainer, 3000);
 *   counter.updateValues(arrayContainer, new long[]{{30, 300}, 3000);
 *
 *   // The values are distributed between states 0 and 1 according to the time
 *   // spent in those respective states. In this specific case, 1000 and 2000 ms.
 *   counter.getValues(arrayContainer, 0);
 *   // arrayContainer now has values {10, 100}
 *   counter.getValues(arrayContainer, 1);
 *   // arrayContainer now has values {20, 200}
 *   counter.getCounts(array, 0);
 *   // array now has values {10, 100}
 *   counter.getCounts(array, 1);
 *   // array now has values {20, 200}
 * </pre>
 *
 * The tracked values are expected to increase monotonically.
@@ -62,110 +58,7 @@ import java.util.concurrent.atomic.AtomicReference;
@RavenwoodKeepWholeClass
@RavenwoodRedirectionClass("LongArrayMultiStateCounter_host")
public final class LongArrayMultiStateCounter implements Parcelable {

    /**
     * Container for a native equivalent of a long[].
     */
    @RavenwoodKeepWholeClass
    @RavenwoodRedirectionClass("LongArrayContainer_host")
    public static class LongArrayContainer {
        private static NativeAllocationRegistry sRegistry;

        // Visible to other objects in this package so that it can be passed to @CriticalNative
        // methods.
        final long mNativeObject;
        private final int mLength;

        public LongArrayContainer(int length) {
            mLength = length;
            mNativeObject = native_init(length);
            registerNativeAllocation();
        }

        @RavenwoodReplace
        private void registerNativeAllocation() {
            if (sRegistry == null) {
                synchronized (LongArrayMultiStateCounter.class) {
                    if (sRegistry == null) {
                        sRegistry = NativeAllocationRegistry.createMalloced(
                                LongArrayContainer.class.getClassLoader(), native_getReleaseFunc());
                    }
                }
            }
            sRegistry.registerNativeAllocation(this, mNativeObject);
        }

        private void registerNativeAllocation$ravenwood() {
            // No-op under ravenwood
        }

        /**
         * Copies the supplied values into the underlying native array.
         */
        public void setValues(long[] array) {
            if (array.length != mLength) {
                throw new IllegalArgumentException(
                        "Invalid array length: " + array.length + ", expected: " + mLength);
            }
            native_setValues(mNativeObject, array);
        }

        /**
         * Copies the underlying native array values to the supplied array.
         */
        public void getValues(long[] array) {
            if (array.length != mLength) {
                throw new IllegalArgumentException(
                        "Invalid array length: " + array.length + ", expected: " + mLength);
            }
            native_getValues(mNativeObject, array);
        }

        /**
         * Combines contained values into a smaller array by aggregating them
         * according to an index map.
         */
        public boolean combineValues(long[] array, int[] indexMap) {
            if (indexMap.length != mLength) {
                throw new IllegalArgumentException(
                        "Wrong index map size " + indexMap.length + ", expected " + mLength);
            }
            return native_combineValues(mNativeObject, array, indexMap);
        }

        @Override
        public String toString() {
            final long[] array = new long[mLength];
            getValues(array);
            return Arrays.toString(array);
        }

        @CriticalNative
        @RavenwoodRedirect
        private static native long native_init(int length);

        @CriticalNative
        @RavenwoodRedirect
        private static native long native_getReleaseFunc();

        @FastNative
        @RavenwoodRedirect
        private static native void native_setValues(long nativeObject, long[] array);

        @FastNative
        @RavenwoodRedirect
        private static native void native_getValues(long nativeObject, long[] array);

        @FastNative
        @RavenwoodRedirect
        private static native boolean native_combineValues(long nativeObject, long[] array,
                int[] indexMap);
    }

    private static volatile NativeAllocationRegistry sRegistry;
    private static final AtomicReference<LongArrayContainer> sTmpArrayContainer =
            new AtomicReference<>();

    private final int mStateCount;
    private final int mLength;

@@ -257,41 +150,14 @@ public final class LongArrayMultiStateCounter implements Parcelable {
            throw new IllegalArgumentException(
                    "Invalid array length: " + values.length + ", expected: " + mLength);
        }
        LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
        if (container == null || container.mLength != values.length) {
            container = new LongArrayContainer(values.length);
        }
        container.setValues(values);
        native_setValues(mNativeObject, state, container.mNativeObject);
        sTmpArrayContainer.set(container);
    }

    /**
     * Sets the new values.  The delta between the previously set values and these values
     * is distributed among the state according to the time the object spent in those states
     * since the previous call to updateValues.
     */
    public void updateValues(long[] values, long timestampMs) {
        LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
        if (container == null || container.mLength != values.length) {
            container = new LongArrayContainer(values.length);
        }
        container.setValues(values);
        updateValues(container, timestampMs);
        sTmpArrayContainer.set(container);
        native_setValues(mNativeObject, state, values);
    }

    /**
     * Adds the supplied values to the current accumulated values in the counter.
     */
    public void incrementValues(long[] values, long timestampMs) {
        LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
        if (container == null || container.mLength != values.length) {
            container = new LongArrayContainer(values.length);
        }
        container.setValues(values);
        native_incrementValues(mNativeObject, container.mNativeObject, timestampMs);
        sTmpArrayContainer.set(container);
        native_incrementValues(mNativeObject, values, timestampMs);
    }

    /**
@@ -299,24 +165,23 @@ public final class LongArrayMultiStateCounter implements Parcelable {
     * is distributed among the state according to the time the object spent in those states
     * since the previous call to updateValues.
     */
    public void updateValues(LongArrayContainer longArrayContainer, long timestampMs) {
        if (longArrayContainer.mLength != mLength) {
    public void updateValues(long[] values, long timestampMs) {
        if (values.length != mLength) {
            throw new IllegalArgumentException(
                    "Invalid array length: " + longArrayContainer.mLength + ", expected: "
                            + mLength);
                    "Invalid array length: " + values.length + ", expected: " + mLength);
        }
        native_updateValues(mNativeObject, longArrayContainer.mNativeObject, timestampMs);
        native_updateValues(mNativeObject, values, timestampMs);
    }

    /**
     * Adds the supplied values to the current accumulated values in the counter.
     */
    public void addCounts(LongArrayContainer counts) {
        if (counts.mLength != mLength) {
    public void addCounts(long[] counts) {
        if (counts.length != mLength) {
            throw new IllegalArgumentException(
                    "Invalid array length: " + counts.mLength + ", expected: " + mLength);
                    "Invalid array length: " + counts.length + ", expected: " + mLength);
        }
        native_addCounts(mNativeObject, counts.mNativeObject);
        native_addCounts(mNativeObject, counts);
    }

    /**
@@ -330,29 +195,15 @@ public final class LongArrayMultiStateCounter implements Parcelable {
     * Populates the array with the accumulated counts for the specified state.
     */
    public void getCounts(long[] counts, int state) {
        LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
        if (container == null || container.mLength != counts.length) {
            container = new LongArrayContainer(counts.length);
        }
        getCounts(container, state);
        container.getValues(counts);
        sTmpArrayContainer.set(container);
    }

    /**
     * Populates longArrayContainer with the accumulated counts for the specified state.
     */
    public void getCounts(LongArrayContainer longArrayContainer, int state) {
        if (state < 0 || state >= mStateCount) {
            throw new IllegalArgumentException(
                    "State: " + state + ", outside the range: [0-" + mStateCount + "]");
        }
        if (longArrayContainer.mLength != mLength) {
        if (counts.length != mLength) {
            throw new IllegalArgumentException(
                    "Invalid array length: " + longArrayContainer.mLength
                            + ", expected: " + mLength);
                    "Invalid array length: " + counts.length + ", expected: " + mLength);
        }
        native_getCounts(mNativeObject, longArrayContainer.mNativeObject, state);
        native_getCounts(mNativeObject, counts, state);
    }

    @Override
@@ -370,8 +221,7 @@ public final class LongArrayMultiStateCounter implements Parcelable {
        return 0;
    }

    public static final Creator<LongArrayMultiStateCounter> CREATOR =
            new Creator<LongArrayMultiStateCounter>() {
    public static final Creator<LongArrayMultiStateCounter> CREATOR = new Creator<>() {
        @Override
        public LongArrayMultiStateCounter createFromParcel(Parcel in) {
            return new LongArrayMultiStateCounter(in);
@@ -406,34 +256,31 @@ public final class LongArrayMultiStateCounter implements Parcelable {
    private static native void native_copyStatesFrom(long nativeObjectTarget,
            long nativeObjectSource);

    @CriticalNative
    @FastNative
    @RavenwoodRedirect
    private static native void native_setValues(long nativeObject, int state,
            long longArrayContainerNativeObject);
    private static native void native_setValues(long nativeObject, int state, long[] values);

    @CriticalNative
    @FastNative
    @RavenwoodRedirect
    private static native void native_updateValues(long nativeObject,
            long longArrayContainerNativeObject, long timestampMs);
    private static native void native_updateValues(long nativeObject, long[] values,
            long timestampMs);

    @CriticalNative
    @FastNative
    @RavenwoodRedirect
    private static native void native_incrementValues(long nativeObject,
            long longArrayContainerNativeObject, long timestampMs);
    private static native void native_incrementValues(long nativeObject, long[] values,
            long timestampMs);

    @CriticalNative
    @FastNative
    @RavenwoodRedirect
    private static native void native_addCounts(long nativeObject,
            long longArrayContainerNativeObject);
    private static native void native_addCounts(long nativeObject, long[] counts);

    @CriticalNative
    @RavenwoodRedirect
    private static native void native_reset(long nativeObject);

    @CriticalNative
    @FastNative
    @RavenwoodRedirect
    private static native void native_getCounts(long nativeObject,
            long longArrayContainerNativeObject, int state);
    private static native void native_getCounts(long nativeObject, long[] counts, int state);

    @FastNative
    @RavenwoodRedirect
+28 −28
Original line number Diff line number Diff line
@@ -49,27 +49,26 @@ static jlongArray getUidCpuFreqTimeMs(JNIEnv *env, jclass, jint uid) {
 * to the supplied multi-state counter in accordance with the counter's state.
 */
static jboolean addCpuTimeInFreqDelta(
        jint uid, jlong counterNativePtr, jlong timestampMs,
        JNIEnv *env, jint uid, jlong counterNativePtr, jlong timestampMs,
        std::optional<std::vector<std::vector<uint64_t>>> timeInFreqDataNanos,
        jlong deltaOutContainerNativePtr) {
        jlongArray deltaOut) {
    if (!timeInFreqDataNanos) {
        return false;
    }

    battery::LongArrayMultiStateCounter *counter =
            reinterpret_cast<battery::LongArrayMultiStateCounter *>(counterNativePtr);
    auto 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();
    battery::Uint64ArrayRW flattened(s);
    uint64_t *out = flattened.dataRW();
    auto offset = out;
    for (const auto &cluster : *timeInFreqDataNanos) {
        flattened.insert(offset, cluster.begin(), cluster.end());
        memcpy(offset, cluster.data(), cluster.size() * sizeof(uint64_t));
        offset += cluster.size();
    }
    for (size_t i = 0; i < s; ++i) {
        flattened[i] /= NSEC_PER_MSEC;
        out[i] /= NSEC_PER_MSEC;
    }
    if (s != counter->getCount(0).size()) { // Counter has at least one state
        ALOGE("Mismatch between eBPF data size (%d) and the counter size (%d)", (int)s,
@@ -77,29 +76,32 @@ static jboolean addCpuTimeInFreqDelta(
        return false;
    }

    const std::vector<uint64_t> &delta = counter->updateValue(flattened, timestampMs);
    if (deltaOutContainerNativePtr) {
        std::vector<uint64_t> *vector =
                reinterpret_cast<std::vector<uint64_t> *>(deltaOutContainerNativePtr);
        *vector = delta;
    const battery::Uint64Array &delta = counter->updateValue(flattened, timestampMs);
    if (deltaOut) {
        ScopedLongArrayRW scopedArray(env, deltaOut);
        uint64_t *array = reinterpret_cast<uint64_t *>(scopedArray.get());
        if (delta.data() != nullptr) {
            memcpy(array, delta.data(), s * sizeof(uint64_t));
        } else {
            memset(array, 0, s * sizeof(uint64_t));
        }
    }

    return true;
}

static jboolean addDeltaFromBpf(jint uid, jlong counterNativePtr, jlong timestampMs,
                                jlong deltaOutContainerNativePtr) {
    return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs,
                                 android::bpf::getUidCpuFreqTimes(uid), deltaOutContainerNativePtr);
static jboolean addDeltaFromBpf(JNIEnv *env, jlong self, jint uid, jlong counterNativePtr,
                                jlong timestampMs, jlongArray deltaOut) {
    return addCpuTimeInFreqDelta(env, uid, counterNativePtr, timestampMs,
                                 android::bpf::getUidCpuFreqTimes(uid), deltaOut);
}

static jboolean addDeltaForTest(JNIEnv *env, jclass, jint uid, jlong counterNativePtr,
                                jlong timestampMs, jobjectArray timeInFreqDataNanos,
                                jlong deltaOutContainerNativePtr) {
                                jlongArray deltaOut) {
    if (!timeInFreqDataNanos) {
        return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs,
                                     std::optional<std::vector<std::vector<uint64_t>>>(),
                                     deltaOutContainerNativePtr);
        return addCpuTimeInFreqDelta(env, uid, counterNativePtr, timestampMs,
                                     std::optional<std::vector<std::vector<uint64_t>>>(), deltaOut);
    }

    std::vector<std::vector<uint64_t>> timeInFreqData;
@@ -113,18 +115,16 @@ static jboolean addDeltaForTest(JNIEnv *env, jclass, jint uid, jlong counterNati
        }
        timeInFreqData.push_back(cluster);
    }
    return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs, std::optional(timeInFreqData),
                                 deltaOutContainerNativePtr);
    return addCpuTimeInFreqDelta(env, uid, counterNativePtr, timestampMs,
                                 std::optional(timeInFreqData), deltaOut);
}

static const JNINativeMethod g_single_methods[] = {
        {"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs},

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

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

int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) {
Loading