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

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

Merge "Add DiskIo to Statsd"

parents e02713e6 e7726dc4
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -178,6 +178,7 @@ message Atom {
        BatteryVoltage battery_voltage = 10030;
        NumFingerprints num_fingerprints = 10031;
        ProcStats proc_stats = 10029;
        DiskIo disk_io = 10032;
    }

    // DO NOT USE field numbers above 100,000 in AOSP.
@@ -2621,6 +2622,30 @@ message CategorySize {
    optional int64 cache_time_millis = 3;
}

/**
 * Pulls per uid I/O stats. The stats are cumulative since boot.
 *
 * Read/write bytes are I/O events from a storage device
 * Read/write chars are data requested by read/write syscalls, and can be
 *   satisfied by caching.
 *
 * Pulled from StatsCompanionService, which reads proc/uid_io/stats.
 */
message DiskIo {
    optional int32 uid = 1 [(is_uid) = true];
    optional int64 fg_chars_read = 2;
    optional int64 fg_chars_write = 3;
    optional int64 fg_bytes_read = 4;
    optional int64 fg_bytes_write = 5;
    optional int64 bg_chars_read = 6;
    optional int64 bg_chars_write = 7;
    optional int64 bg_bytes_read = 8;
    optional int64 bg_bytes_write = 9;
    optional int64 fg_fsync = 10;
    optional int64 bg_fsync= 11;
}


/**
 * Pulls the number of fingerprints for each user.
 *
+6 −0
Original line number Diff line number Diff line
@@ -211,6 +211,12 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
        // ProcStats.
        {android::util::PROC_STATS,
         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
        // Disk I/O stats per uid.
        {android::util::DISK_IO,
         {{2,3,4,5,6,7,8,9,10,11},
          {},
          3 * NS_PER_SEC,
          new StatsCompanionServicePuller(android::util::DISK_IO)}},
};

StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
+113 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.text.TextUtils;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;


/**
 * Reads /proc/uid_io/stats which has the line format:
 *
 * uid: foreground_read_chars foreground_write_chars foreground_read_bytes foreground_write_bytes
 * background_read_chars background_write_chars background_read_bytes background_write_bytes
 * foreground_fsync background_fsync
 *
 * This provides the number of bytes/chars read/written in foreground/background for each uid.
 * The file contains a monotonically increasing count of bytes/chars for a single boot.
 */
public class StoragedUidIoStatsReader {

    private static final String TAG = StoragedUidIoStatsReader.class.getSimpleName();
    private static String sUidIoFile = "/proc/uid_io/stats";

    public StoragedUidIoStatsReader() {
    }

    @VisibleForTesting
    public StoragedUidIoStatsReader(String file) {
        sUidIoFile = file;
    }

    /**
     * Notifies when new data is available.
     */
    public interface Callback {

        /**
         * Provides data to the client.
         *
         * Note: Bytes are I/O events from a storage device. Chars are data requested by syscalls,
         *   and can be satisfied by caching.
         */
        void onUidStorageStats(int uid, long fgCharsRead, long fgCharsWrite, long fgBytesRead,
                long fgBytesWrite, long bgCharsRead, long bgCharsWrite, long bgBytesRead,
                long bgBytesWrite, long fgFsync, long bgFsync);
    }

    /**
     * Reads the proc file, calling into the callback with raw absolute value of I/O stats
     * for each UID.
     *
     * @param callback The callback to invoke for each line of the proc file.
     */
    public void readAbsolute(Callback callback) {
        final int oldMask = StrictMode.allowThreadDiskReadsMask();
        File file = new File(sUidIoFile);
        try (BufferedReader reader = Files.newBufferedReader(file.toPath())) {
            String line;
            while ((line = reader.readLine()) != null) {
                String[] fields = TextUtils.split(line, " ");
                if (fields.length != 11) {
                    Slog.e(TAG, "Malformed entry in " + sUidIoFile + ": " + line);
                    continue;
                }
                try {
                    final String uidStr = fields[0];
                    final int uid = Integer.parseInt(fields[0], 10);
                    final long fgCharsRead = Long.parseLong(fields[1], 10);
                    final long fgCharsWrite = Long.parseLong(fields[2], 10);
                    final long fgBytesRead = Long.parseLong(fields[3], 10);
                    final long fgBytesWrite = Long.parseLong(fields[4], 10);
                    final long bgCharsRead = Long.parseLong(fields[5], 10);
                    final long bgCharsWrite = Long.parseLong(fields[6], 10);
                    final long bgBytesRead = Long.parseLong(fields[7], 10);
                    final long bgBytesWrite = Long.parseLong(fields[8], 10);
                    final long fgFsync = Long.parseLong(fields[9], 10);
                    final long bgFsync = Long.parseLong(fields[10], 10);
                    callback.onUidStorageStats(uid, fgCharsRead, fgCharsWrite, fgBytesRead,
                            fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite,
                            fgFsync, bgFsync);
                } catch (NumberFormatException e) {
                    Slog.e(TAG, "Could not parse entry in " + sUidIoFile + ": " + e.getMessage());
                }
            }
        } catch (IOException e) {
            Slog.e(TAG, "Failed to read " + sUidIoFile + ": " + e.getMessage());
        } finally {
            StrictMode.setThreadPolicyMask(oldMask);
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@ import java.util.Random;
/**
 * Test class for {@link KernelCpuProcReader}.
 *
 * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReader
 * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReaderTest
 */
@SmallTest
@RunWith(AndroidJUnit4.class)
+171 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;

import android.content.Context;
import android.os.FileUtils;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.io.BufferedWriter;
import java.io.File;
import java.nio.file.Files;


/**
 * Test class for {@link StoragedUidIoStatsReader}.
 *
 * To run it:
 * atest FrameworksCoreTests:com.android.internal.os.StoragedUidIoStatsReaderTest
 */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StoragedUidIoStatsReaderTest {

    private File mRoot;
    private File mTestDir;
    private File mTestFile;
    // private Random mRand = new Random();

    private StoragedUidIoStatsReader mStoragedUidIoStatsReader;
    @Mock
    private StoragedUidIoStatsReader.Callback mCallback;

    private Context getContext() {
        return InstrumentationRegistry.getContext();
    }

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
        mRoot = getContext().getFilesDir();
        mTestFile = new File(mTestDir, "test.file");
        mStoragedUidIoStatsReader = new StoragedUidIoStatsReader(mTestFile.getAbsolutePath());
    }

    @After
    public void tearDown() throws Exception {
        FileUtils.deleteContents(mTestDir);
        FileUtils.deleteContents(mRoot);
    }


    /**
     * Tests that reading will never call the callback.
     */
    @Test
    public void testReadNonexistentFile() throws Exception {
        mStoragedUidIoStatsReader.readAbsolute(mCallback);
        verifyZeroInteractions(mCallback);

    }

    /**
     * Tests that reading a file with 3 uids works as expected.
     */
    @Test
    public void testReadExpected() throws Exception {
        BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath());
        int[] uids = {0, 100, 200};
        long[] fg_chars_read = {1L, 101L, 201L};
        long[] fg_chars_write = {2L, 102L, 202L};
        long[] fg_bytes_read = {3L, 103L, 203L};
        long[] fg_bytes_write = {4L, 104L, 204L};
        long[] bg_chars_read = {5L, 105L, 205L};
        long[] bg_chars_write = {6L, 106L, 206L};
        long[] bg_bytes_read = {7L, 107L, 207L};
        long[] bg_bytes_write = {8L, 108L, 208L};
        long[] fg_fsync = {9L, 109L, 209L};
        long[] bg_fsync = {10L, 110L, 210L};

        for (int i = 0; i < uids.length; i++) {
            bufferedWriter.write(String
                    .format("%d %d %d %d %d %d %d %d %d %d %d\n", uids[i], fg_chars_read[i],
                            fg_chars_write[i], fg_bytes_read[i], fg_bytes_write[i],
                            bg_chars_read[i], bg_chars_write[i], bg_bytes_read[i],
                            bg_bytes_write[i], fg_fsync[i], bg_fsync[i]));
        }
        bufferedWriter.close();

        mStoragedUidIoStatsReader.readAbsolute(mCallback);
        for (int i = 0; i < uids.length; i++) {
            verify(mCallback).onUidStorageStats(uids[i], fg_chars_read[i], fg_chars_write[i],
                    fg_bytes_read[i], fg_bytes_write[i], bg_chars_read[i], bg_chars_write[i],
                    bg_bytes_read[i], bg_bytes_write[i], fg_fsync[i], bg_fsync[i]);
        }
        verifyNoMoreInteractions(mCallback);

    }

    /**
     * Tests that a line with less than 11 items is passed over.
     */
    @Test
    public void testLineDoesNotElevenEntries() throws Exception {
        BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath());

        // Only has 10 numbers.
        bufferedWriter.write(String
                .format("%d %d %d %d %d %d %d %d %d %d\n", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9));

        bufferedWriter.write(String
                .format("%d %d %d %d %d %d %d %d %d %d %d\n", 10, 11, 12, 13, 14, 15, 16, 17, 18,
                        19, 20));
        bufferedWriter.close();

        // Make sure we get the second line, but the first is skipped.
        mStoragedUidIoStatsReader.readAbsolute(mCallback);
        verify(mCallback).onUidStorageStats(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
        verifyNoMoreInteractions(mCallback);
    }


    /**
     * Tests that a line that is malformed is passed over.
     */
    @Test
    public void testLineIsMalformed() throws Exception {
        BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath());

        // Line is not formatted properly. It has a string.
        bufferedWriter.write(String
                .format("%d %d %d %d %d %s %d %d %d %d %d\n", 0, 1, 2, 3, 4, "NotANumber", 5, 6, 7,
                        8, 9));

        bufferedWriter.write(String
                .format("%d %d %d %d %d %d %d %d %d %d %d\n", 10, 11, 12, 13, 14, 15, 16, 17, 18,
                        19, 20));
        bufferedWriter.close();

        // Make sure we get the second line, but the first is skipped.
        mStoragedUidIoStatsReader.readAbsolute(mCallback);
        verify(mCallback).onUidStorageStats(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
        verifyNoMoreInteractions(mCallback);
    }
}
Loading