diff options
| -rw-r--r-- | core/java/com/android/server/BootReceiver.java | 104 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java | 111 |
2 files changed, 209 insertions, 6 deletions
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index 4a9a2c55e940..c1f443f62cfc 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -35,6 +35,7 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.Xml; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; @@ -89,6 +90,11 @@ public class BootReceiver extends BroadcastReceiver { // example: fs_stat,/dev/block/platform/soc/by-name/userdata,0x5 private static final String FS_STAT_PATTERN = "fs_stat,[^,]*/([^/,]+),(0x[0-9a-fA-F]+)"; + private static final int FS_STAT_FS_FIXED = 0x400; // should match with fs_mgr.cpp:FsStatFlags + private static final String FSCK_PASS_PATTERN = "Pass ([1-9]E?):"; + private static final String FSCK_TREE_OPTIMIZATION_PATTERN = + "Inode [0-9]+ extent tree.*could be shorter"; + private static final String FSCK_FS_MODIFIED = "FILE SYSTEM WAS MODIFIED"; // ro.boottime.init.mount_all. + postfix for mount_all duration private static final String[] MOUNT_DURATION_PROPS_POSTFIX = new String[] { "early", "default", "late" }; @@ -334,17 +340,22 @@ public class BootReceiver extends BroadcastReceiver { String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"); Pattern pattern = Pattern.compile(FS_STAT_PATTERN); - for (String line : log.split("\n")) { // should check all lines - if (line.contains("FILE SYSTEM WAS MODIFIED")) { + String lines[] = log.split("\n"); + int lineNumber = 0; + int lastFsStatLineNumber = 0; + for (String line : lines) { // should check all lines + if (line.contains(FSCK_FS_MODIFIED)) { uploadNeeded = true; } else if (line.contains("fs_stat")){ Matcher matcher = pattern.matcher(line); if (matcher.find()) { - handleFsckFsStat(matcher); + handleFsckFsStat(matcher, lines, lastFsStatLineNumber, lineNumber); + lastFsStatLineNumber = lineNumber; } else { Slog.w(TAG, "cannot parse fs_stat:" + line); } } + lineNumber++; } if (uploadEnabled && uploadNeeded ) { @@ -403,7 +414,88 @@ public class BootReceiver extends BroadcastReceiver { } } - private static void handleFsckFsStat(Matcher match) { + /** + * Fix fs_stat from e2fsck. + * For now, only handle the case of quota warning caused by tree optimization. Clear fs fix + * flag (=0x400) caused by that. + * + * @param partition partition name + * @param statOrg original stat reported from e2fsck log + * @param lines e2fsck logs broken down into lines + * @param startLineNumber start line to parse + * @param endLineNumber end line. exclusive. + * @return updated fs_stat. For tree optimization, will clear bit 0x400. + */ + @VisibleForTesting + public static int fixFsckFsStat(String partition, int statOrg, String[] lines, + int startLineNumber, int endLineNumber) { + int stat = statOrg; + if ((stat & FS_STAT_FS_FIXED) != 0) { + // fs was fixed. should check if quota warning was caused by tree optimization. + // This is not a real fix but optimization, so should not be counted as a fs fix. + Pattern passPattern = Pattern.compile(FSCK_PASS_PATTERN); + Pattern treeOptPattern = Pattern.compile(FSCK_TREE_OPTIMIZATION_PATTERN); + String currentPass = ""; + boolean foundTreeOptimization = false; + boolean foundQuotaFix = false; + boolean foundOtherFix = false; + String otherFixLine = null; + for (int i = startLineNumber; i < endLineNumber; i++) { + String line = lines[i]; + if (line.contains(FSCK_FS_MODIFIED)) { // no need to parse above this + break; + } else if (line.startsWith("Pass ")) { + Matcher matcher = passPattern.matcher(line); + if (matcher.find()) { + currentPass = matcher.group(1); + } + } else if (line.startsWith("Inode ")) { + Matcher matcher = treeOptPattern.matcher(line); + if (matcher.find() && currentPass.equals("1")) { + foundTreeOptimization = true; + Slog.i(TAG, "fs_stat, partition:" + partition + " found tree optimization:" + + line); + } else { + foundOtherFix = true; + otherFixLine = line; + break; + } + } else if (line.startsWith("[QUOTA WARNING]") && currentPass.equals("5")) { + Slog.i(TAG, "fs_stat, partition:" + partition + " found quota warning:" + + line); + foundQuotaFix = true; + if (!foundTreeOptimization) { // only quota warning, this is real fix. + otherFixLine = line; + break; + } + } else if (line.startsWith("Update quota info") && currentPass.equals("5")) { + // follows "[QUOTA WARNING]", ignore + } else { + line = line.trim(); + // ignore empty msg or any msg before Pass 1 + if (!line.isEmpty() && !currentPass.isEmpty()) { + foundOtherFix = true; + otherFixLine = line; + break; + } + } + } + if (!foundOtherFix && foundTreeOptimization && foundQuotaFix) { + // not a real fix, so clear it. + Slog.i(TAG, "fs_stat, partition:" + partition + + " quota fix due to tree optimization"); + stat &= ~FS_STAT_FS_FIXED; + } else { + if (otherFixLine != null) { + Slog.i(TAG, "fs_stat, partition:" + partition + " fix:" + otherFixLine); + } + } + } + return stat; + } + + private static void handleFsckFsStat(Matcher match, String[] lines, int startLineNumber, + int endLineNumber) { String partition = match.group(1); int stat; try { @@ -412,9 +504,9 @@ public class BootReceiver extends BroadcastReceiver { Slog.w(TAG, "cannot parse fs_stat: partition:" + partition + " stat:" + match.group(2)); return; } - + stat = fixFsckFsStat(partition, stat, lines, startLineNumber, endLineNumber); MetricsLogger.histogram(null, "boot_fs_stat_" + partition, stat); - Slog.i(TAG, "fs_stat, partition:" + partition + " stat:" + match.group(2)); + Slog.i(TAG, "fs_stat, partition:" + partition + " stat:0x" + Integer.toHexString(stat)); } private static HashMap<String, Long> readTimestamps() { diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java new file mode 100644 index 000000000000..362c47ad34a4 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2017 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.server; + +import static junit.framework.Assert.*; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import junit.framework.Assert; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BootReceiverFixFsckFsStatTest { + + private static final String PARTITION = "userdata"; + + @Test + public void testTreeOptimization() { + final String[] logs = { + "e2fsck 1.43.3 (04-Sep-2016)", + "Pass 1: Checking inodes, blocks, and sizes", + "Inode 877141 extent tree (at level 1) could be shorter. Fix? yes", + " ", + "Pass 1E: Optimizing extent trees", + "Pass 2: Checking directory structure", + "Pass 3: Checking directory connectivity", + "Pass 4: Checking reference counts", + "Pass 5: Checking group summary information", + "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (71667712, 1000) != expected (71671808, 1000)", + "Update quota info for quota type 0? yes", + " ", + "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (59555840, 953) != expected (59559936, 953)", + "Update quota info for quota type 1? yes", + " ", + "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****" + }; + doTestFsckFsStat(logs, 0x405, 5, 0, logs.length); + + final String[] doubleLogs = new String[logs.length * 2]; + System.arraycopy(logs, 0, doubleLogs, 0, logs.length); + System.arraycopy(logs, 0, doubleLogs, logs.length, logs.length); + doTestFsckFsStat(doubleLogs, 0x401, 1, 0, logs.length); + doTestFsckFsStat(doubleLogs, 0x402, 2, logs.length, logs.length * 2); + } + + @Test + public void testQuotaOnly() { + final String[] logs = { + "e2fsck 1.43.3 (04-Sep-2016)", + "Pass 1: Checking inodes, blocks, and sizes", + "Pass 1E: Optimizing extent trees", + "Pass 2: Checking directory structure", + "Pass 3: Checking directory connectivity", + "Pass 4: Checking reference counts", + "Pass 5: Checking group summary information", + "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (71667712, 1000) != expected (71671808, 1000)", + "Update quota info for quota type 0? yes", + " ", + "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (59555840, 953) != expected (59559936, 953)", + "Update quota info for quota type 1? yes", + " ", + "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****" + }; + doTestFsckFsStat(logs, 0x405, 0x405, 0, logs.length); + } + + @Test + public void testOrphaned() { + final String[] logs = { + "e2fsck 1.43.3 (04-Sep-2016)", + "Pass 1: Checking inodes, blocks, and sizes", + "Inodes that were part of a corrupted orphan linked list found. Fix? yes", + " ", + "Inode 589877 was part of the orphaned inode list. FIXED.", + " ", + "Inode 589878 was part of the orphaned inode list. FIXED.", + " ", + "Pass 2: Checking directory structure", + "Pass 3: Checking directory connectivity", + "Pass 4: Checking reference counts", + "Pass 5: Checking group summary information", + " ", + "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****" + }; + doTestFsckFsStat(logs, 0x405, 0x405, 0, logs.length); + } + + private void doTestFsckFsStat(String[] lines, int statOrg, int statUpdated, int startLineNumber, + int endLineNumber) { + assertEquals(statUpdated, BootReceiver.fixFsckFsStat(PARTITION, statOrg, lines, + startLineNumber, endLineNumber)); + } +} |