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

Commit ca027393 authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Reduce risk of a long lock contention during getBatteryUsageStats

Bug: 337187684
Test: atest PowerStatsTests; atest PowerStatsTestsRavenwood
Flag: com.android.server.power.optimization.streamlined_battery_stats

Change-Id: I0be01da64b990875e1dc6054fe49611a92c8013b
parent 79db3ce4
Loading
Loading
Loading
Loading
+60 −22
Original line number Diff line number Diff line
@@ -133,6 +133,13 @@ public class BatteryStatsHistory {
    // For state2, trace all bit changes.
    static final int STATE2_TRACE_MASK = ~0;

    /**
     * Number of overflow bytes that can be written into the history buffer if the history
     * directory is locked. This is done to prevent a long lock contention and a potential
     * kill by a watchdog.
     */
    private static final int EXTRA_BUFFER_SIZE_WHEN_DIR_LOCKED = 100_000;

    private final Parcel mHistoryBuffer;
    private final File mSystemDir;
    private final HistoryStepDetailsCalculator mStepDetailsCalculator;
@@ -260,6 +267,10 @@ public class BatteryStatsHistory {
            mLock.lock();
        }

        boolean tryLock() {
            return mLock.tryLock();
        }

        void unlock() {
            mLock.unlock();
            if (mCleanupNeeded) {
@@ -469,14 +480,12 @@ public class BatteryStatsHistory {
                return;
            }

            if (isLocked()) {
            if (!tryLock()) {
                mCleanupNeeded = true;
                return;
            }

            mCleanupNeeded = false;

            lock();
            try {
                // if free disk space is less than 100MB, delete oldest history file.
                if (!hasFreeDiskSpace(mDirectory)) {
@@ -1772,29 +1781,12 @@ public class BatteryStatsHistory {
            }
            mHistoryLastWritten.setTo(mHistoryLastLastWritten);
        }
        final int dataSize = mHistoryBuffer.dataSize();

        if (dataSize >= mMaxHistoryBufferSize) {
            if (mMaxHistoryBufferSize == 0) {
                Slog.wtf(TAG, "mMaxHistoryBufferSize should not be zero when writing history");
                mMaxHistoryBufferSize = 1024;
            }

            // Make a copy of mHistoryCur.
            HistoryItem copy = new HistoryItem();
            copy.setTo(cur);

            startNextFile(elapsedRealtimeMs);

            // startRecordingHistory will reset mHistoryCur.
            startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);

            // Add the copy into history buffer.
            writeHistoryItem(elapsedRealtimeMs, uptimeMs, copy, HistoryItem.CMD_UPDATE);
        if (maybeFlushBufferAndWriteHistoryItem(cur, elapsedRealtimeMs, uptimeMs)) {
            return;
        }

        if (dataSize == 0) {
        if (mHistoryBuffer.dataSize() == 0) {
            // The history is currently empty; we need it to start with a time stamp.
            HistoryItem copy = new HistoryItem();
            copy.setTo(cur);
@@ -1811,6 +1803,52 @@ public class BatteryStatsHistory {
        writeHistoryItem(elapsedRealtimeMs, uptimeMs, cur, HistoryItem.CMD_UPDATE);
    }

    @GuardedBy("this")
    private boolean maybeFlushBufferAndWriteHistoryItem(HistoryItem cur, long elapsedRealtimeMs,
            long uptimeMs) {
        int dataSize = mHistoryBuffer.dataSize();
        if (dataSize < mMaxHistoryBufferSize) {
            return false;
        }

        if (mMaxHistoryBufferSize == 0) {
            Slog.wtf(TAG, "mMaxHistoryBufferSize should not be zero when writing history");
            mMaxHistoryBufferSize = 1024;
        }

        boolean successfullyLocked = mHistoryDir.tryLock();
        if (!successfullyLocked) {      // Already locked by another thread
            // If the buffer size is below the allowed overflow limit, just keep going
            if (dataSize < mMaxHistoryBufferSize + EXTRA_BUFFER_SIZE_WHEN_DIR_LOCKED) {
                return false;
            }

            // Report the long contention as a WTF and flush the buffer anyway, potentially
            // triggering a watchdog kill, which is still better than spinning forever.
            Slog.wtf(TAG, "History buffer overflow exceeds " + EXTRA_BUFFER_SIZE_WHEN_DIR_LOCKED
                    + " bytes");
        }

        // Make a copy of mHistoryCur before starting a new file
        HistoryItem copy = new HistoryItem();
        copy.setTo(cur);

        try {
            startNextFile(elapsedRealtimeMs);
        } finally {
            if (successfullyLocked) {
                mHistoryDir.unlock();
            }
        }

        // startRecordingHistory will reset mHistoryCur.
        startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);

        // Add the copy into history buffer.
        writeHistoryItem(elapsedRealtimeMs, uptimeMs, copy, HistoryItem.CMD_UPDATE);
        return true;
    }

    @GuardedBy("this")
    private void writeHistoryItem(long elapsedRealtimeMs,
            @SuppressWarnings("UnusedVariable") long uptimeMs, HistoryItem cur, byte cmd) {