Loading core/java/com/android/internal/os/ProcTimeInStateReader.java +52 −33 Original line number Diff line number Diff line Loading @@ -19,9 +19,15 @@ package com.android.internal.os; import android.annotation.Nullable; import android.os.Process; import com.android.internal.util.ArrayUtils; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Reads and parses {@code time_in_state} files in the {@code proc} filesystem. Loading @@ -43,6 +49,17 @@ import java.nio.file.Path; * * This file would indicate that the CPU has spent 30 milliseconds at frequency 300,000KHz (300Mhz) * and 10 milliseconds at frequency 1,900,800KHz (1.9GHz). * * <p>This class will also read {@code time_in_state} files with headers, such as: * <pre> * cpu0 * 300000 3 * 364800 0 * ... * cpu4 * 300000 1 * 364800 4 * </pre> */ public class ProcTimeInStateReader { private static final String TAG = "ProcTimeInStateReader"; Loading @@ -51,24 +68,28 @@ public class ProcTimeInStateReader { * The format of a single line of the {@code time_in_state} file that exports the frequency * values */ private static final int[] TIME_IN_STATE_LINE_FREQUENCY_FORMAT = { private static final List<Integer> TIME_IN_STATE_LINE_FREQUENCY_FORMAT = Arrays.asList( Process.PROC_OUT_LONG | Process.PROC_SPACE_TERM, Process.PROC_NEWLINE_TERM, }; Process.PROC_NEWLINE_TERM ); /** * The format of a single line of the {@code time_in_state} file that exports the time values */ private static final int[] TIME_IN_STATE_LINE_TIME_FORMAT = { private static final List<Integer> TIME_IN_STATE_LINE_TIME_FORMAT = Arrays.asList( Process.PROC_SPACE_TERM, Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM, }; Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM ); /** * The format of the {@code time_in_state} file, defined using {@link Process}'s {@code * PROC_OUT_LONG} and related variables * * Defined on first successful read of {@code time_in_state} file. * The format of a header line of the {@code time_in_state} file */ private static final List<Integer> TIME_IN_STATE_HEADER_LINE_FORMAT = Collections.singletonList(Process.PROC_NEWLINE_TERM); /** * The format of the {@code time_in_state} file to extract times, defined using {@link * Process}'s {@code PROC_OUT_LONG} and related variables */ private int[] mTimeInStateTimeFormat; Loading Loading @@ -141,46 +162,44 @@ public class ProcTimeInStateReader { // Read the bytes of the `time_in_state` file byte[] timeInStateBytes = Files.readAllBytes(timeInStatePath); // The number of lines in the `time_in_state` file is the number of frequencies available // Iterate over the lines of the time_in_state file, for each one adding a line to the // formats. These formats are used to extract either the frequencies or the times from a // time_in_state file // Also check if each line is a header, and handle this in the created format arrays ArrayList<Integer> timeInStateFrequencyFormat = new ArrayList<>(); ArrayList<Integer> timeInStateTimeFormat = new ArrayList<>(); int numFrequencies = 0; for (int i = 0; i < timeInStateBytes.length; i++) { if (timeInStateBytes[i] == '\n') { // If the first character of the line is not a digit, we treat it as a header if (!Character.isDigit(timeInStateBytes[i])) { timeInStateFrequencyFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT); timeInStateTimeFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT); } else { timeInStateFrequencyFormat.addAll(TIME_IN_STATE_LINE_FREQUENCY_FORMAT); timeInStateTimeFormat.addAll(TIME_IN_STATE_LINE_TIME_FORMAT); numFrequencies++; } // Go to the next line while (i < timeInStateBytes.length && timeInStateBytes[i] != '\n') { i++; } if (numFrequencies == 0) { throw new IOException("Empty time_in_state file"); } // Set `mTimeInStateTimeFormat` and `timeInStateFrequencyFormat` to the correct length, and // then copy in the `TIME_IN_STATE_{FREQUENCY,TIME}_LINE_FORMAT` until it's full. As we only // use the frequency format in this method, it is not an member variable. final int[] timeInStateTimeFormat = new int[numFrequencies * TIME_IN_STATE_LINE_TIME_FORMAT.length]; final int[] timeInStateFrequencyFormat = new int[numFrequencies * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length]; for (int i = 0; i < numFrequencies; i++) { System.arraycopy( TIME_IN_STATE_LINE_FREQUENCY_FORMAT, 0, timeInStateFrequencyFormat, i * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length, TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length); System.arraycopy( TIME_IN_STATE_LINE_TIME_FORMAT, 0, timeInStateTimeFormat, i * TIME_IN_STATE_LINE_TIME_FORMAT.length, TIME_IN_STATE_LINE_TIME_FORMAT.length); if (numFrequencies == 0) { throw new IOException("Empty time_in_state file"); } // Read the frequencies from the `time_in_state` file and store them, as they will be the // same for every `time_in_state` file final long[] readLongs = new long[numFrequencies]; final boolean readSuccess = Process.parseProcLine( timeInStateBytes, 0, timeInStateBytes.length, timeInStateFrequencyFormat, null, readLongs, null); timeInStateBytes, 0, timeInStateBytes.length, ArrayUtils.convertToIntArray(timeInStateFrequencyFormat), null, readLongs, null); if (!readSuccess) { throw new IOException("Failed to parse time_in_state file"); } mTimeInStateTimeFormat = timeInStateTimeFormat; mTimeInStateTimeFormat = ArrayUtils.convertToIntArray(timeInStateTimeFormat); mFrequenciesKhz = readLongs; } } core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java +17 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,23 @@ public class ProcTimeInStateReaderTest { reader.getUsageTimesMillis(initialTimeInStateFile)); } @Test public void testHeaderFormat() throws IOException { final Path initialTimeInStateFile = mProcDirectory.toPath().resolve( "initial-time-in-state"); Files.write(initialTimeInStateFile, "header1\n1 2\nheader2:\n3 4\n5 6\n7 8\n".getBytes()); final ProcTimeInStateReader reader = new ProcTimeInStateReader(initialTimeInStateFile); assertArrayEquals( "Reported frequencies are correct", new long[]{1, 3, 5, 7}, reader.getFrequenciesKhz()); assertArrayEquals( "Reported usage times are correct", new long[]{20, 40, 60, 80}, reader.getUsageTimesMillis(initialTimeInStateFile)); } @Test public void testDifferentFile() throws IOException { Path initialTimeInStateFile = mProcDirectory.toPath().resolve("initial-time-in-state"); Loading Loading
core/java/com/android/internal/os/ProcTimeInStateReader.java +52 −33 Original line number Diff line number Diff line Loading @@ -19,9 +19,15 @@ package com.android.internal.os; import android.annotation.Nullable; import android.os.Process; import com.android.internal.util.ArrayUtils; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Reads and parses {@code time_in_state} files in the {@code proc} filesystem. Loading @@ -43,6 +49,17 @@ import java.nio.file.Path; * * This file would indicate that the CPU has spent 30 milliseconds at frequency 300,000KHz (300Mhz) * and 10 milliseconds at frequency 1,900,800KHz (1.9GHz). * * <p>This class will also read {@code time_in_state} files with headers, such as: * <pre> * cpu0 * 300000 3 * 364800 0 * ... * cpu4 * 300000 1 * 364800 4 * </pre> */ public class ProcTimeInStateReader { private static final String TAG = "ProcTimeInStateReader"; Loading @@ -51,24 +68,28 @@ public class ProcTimeInStateReader { * The format of a single line of the {@code time_in_state} file that exports the frequency * values */ private static final int[] TIME_IN_STATE_LINE_FREQUENCY_FORMAT = { private static final List<Integer> TIME_IN_STATE_LINE_FREQUENCY_FORMAT = Arrays.asList( Process.PROC_OUT_LONG | Process.PROC_SPACE_TERM, Process.PROC_NEWLINE_TERM, }; Process.PROC_NEWLINE_TERM ); /** * The format of a single line of the {@code time_in_state} file that exports the time values */ private static final int[] TIME_IN_STATE_LINE_TIME_FORMAT = { private static final List<Integer> TIME_IN_STATE_LINE_TIME_FORMAT = Arrays.asList( Process.PROC_SPACE_TERM, Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM, }; Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM ); /** * The format of the {@code time_in_state} file, defined using {@link Process}'s {@code * PROC_OUT_LONG} and related variables * * Defined on first successful read of {@code time_in_state} file. * The format of a header line of the {@code time_in_state} file */ private static final List<Integer> TIME_IN_STATE_HEADER_LINE_FORMAT = Collections.singletonList(Process.PROC_NEWLINE_TERM); /** * The format of the {@code time_in_state} file to extract times, defined using {@link * Process}'s {@code PROC_OUT_LONG} and related variables */ private int[] mTimeInStateTimeFormat; Loading Loading @@ -141,46 +162,44 @@ public class ProcTimeInStateReader { // Read the bytes of the `time_in_state` file byte[] timeInStateBytes = Files.readAllBytes(timeInStatePath); // The number of lines in the `time_in_state` file is the number of frequencies available // Iterate over the lines of the time_in_state file, for each one adding a line to the // formats. These formats are used to extract either the frequencies or the times from a // time_in_state file // Also check if each line is a header, and handle this in the created format arrays ArrayList<Integer> timeInStateFrequencyFormat = new ArrayList<>(); ArrayList<Integer> timeInStateTimeFormat = new ArrayList<>(); int numFrequencies = 0; for (int i = 0; i < timeInStateBytes.length; i++) { if (timeInStateBytes[i] == '\n') { // If the first character of the line is not a digit, we treat it as a header if (!Character.isDigit(timeInStateBytes[i])) { timeInStateFrequencyFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT); timeInStateTimeFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT); } else { timeInStateFrequencyFormat.addAll(TIME_IN_STATE_LINE_FREQUENCY_FORMAT); timeInStateTimeFormat.addAll(TIME_IN_STATE_LINE_TIME_FORMAT); numFrequencies++; } // Go to the next line while (i < timeInStateBytes.length && timeInStateBytes[i] != '\n') { i++; } if (numFrequencies == 0) { throw new IOException("Empty time_in_state file"); } // Set `mTimeInStateTimeFormat` and `timeInStateFrequencyFormat` to the correct length, and // then copy in the `TIME_IN_STATE_{FREQUENCY,TIME}_LINE_FORMAT` until it's full. As we only // use the frequency format in this method, it is not an member variable. final int[] timeInStateTimeFormat = new int[numFrequencies * TIME_IN_STATE_LINE_TIME_FORMAT.length]; final int[] timeInStateFrequencyFormat = new int[numFrequencies * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length]; for (int i = 0; i < numFrequencies; i++) { System.arraycopy( TIME_IN_STATE_LINE_FREQUENCY_FORMAT, 0, timeInStateFrequencyFormat, i * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length, TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length); System.arraycopy( TIME_IN_STATE_LINE_TIME_FORMAT, 0, timeInStateTimeFormat, i * TIME_IN_STATE_LINE_TIME_FORMAT.length, TIME_IN_STATE_LINE_TIME_FORMAT.length); if (numFrequencies == 0) { throw new IOException("Empty time_in_state file"); } // Read the frequencies from the `time_in_state` file and store them, as they will be the // same for every `time_in_state` file final long[] readLongs = new long[numFrequencies]; final boolean readSuccess = Process.parseProcLine( timeInStateBytes, 0, timeInStateBytes.length, timeInStateFrequencyFormat, null, readLongs, null); timeInStateBytes, 0, timeInStateBytes.length, ArrayUtils.convertToIntArray(timeInStateFrequencyFormat), null, readLongs, null); if (!readSuccess) { throw new IOException("Failed to parse time_in_state file"); } mTimeInStateTimeFormat = timeInStateTimeFormat; mTimeInStateTimeFormat = ArrayUtils.convertToIntArray(timeInStateTimeFormat); mFrequenciesKhz = readLongs; } }
core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java +17 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,23 @@ public class ProcTimeInStateReaderTest { reader.getUsageTimesMillis(initialTimeInStateFile)); } @Test public void testHeaderFormat() throws IOException { final Path initialTimeInStateFile = mProcDirectory.toPath().resolve( "initial-time-in-state"); Files.write(initialTimeInStateFile, "header1\n1 2\nheader2:\n3 4\n5 6\n7 8\n".getBytes()); final ProcTimeInStateReader reader = new ProcTimeInStateReader(initialTimeInStateFile); assertArrayEquals( "Reported frequencies are correct", new long[]{1, 3, 5, 7}, reader.getFrequenciesKhz()); assertArrayEquals( "Reported usage times are correct", new long[]{20, 40, 60, 80}, reader.getUsageTimesMillis(initialTimeInStateFile)); } @Test public void testDifferentFile() throws IOException { Path initialTimeInStateFile = mProcDirectory.toPath().resolve("initial-time-in-state"); Loading