1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.loganalysis.parser;
17 
18 import com.android.loganalysis.item.WakelockItem;
19 import com.android.loganalysis.item.WakelockItem.WakeLockCategory;
20 import com.android.loganalysis.util.NumberFormattingUtil;
21 
22 import java.util.List;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25 
26 /**
27  * A {@link IParser} to handle the parsing of wakelock information
28  */
29 public class WakelockParser implements IParser {
30 
31     private static final String WAKE_LOCK_PAT_SUFFIX =
32             "(?:(\\d+)d)?\\s?(?:(\\d+)h)?\\s?(?:(\\d+)m)?\\s?(?:(\\d+)s)?\\s?(?:(\\d+)ms)?"
33             + "\\s?\\((\\d+) times\\)(?: max=\\d+)? realtime";
34 
35     /**
36      * Match a valid line such as:
37      * "  Kernel Wake lock PowerManagerService.WakeLocks: 1h 13m 50s 950ms (2858 times) realtime"
38      */
39     private static final Pattern KERNEL_WAKE_LOCK_PAT = Pattern.compile(
40             "^\\s*Kernel Wake lock (.+): " + WAKE_LOCK_PAT_SUFFIX);
41     /**
42      * Match a valid line such as:
43      * "  Wake lock u0a7 NlpWakeLock: 8m 13s 203ms (1479 times) realtime";
44      */
45     private static final Pattern PARTIAL_WAKE_LOCK_PAT = Pattern.compile(
46             "^\\s*Wake lock (.*)\\s(.+): " + WAKE_LOCK_PAT_SUFFIX);
47 
48     private WakelockItem mItem = new WakelockItem();
49 
50     public static final int TOP_WAKELOCK_COUNT = 5;
51 
52     /**
53      * {@inheritDoc}
54      */
55     @Override
parse(List<String> lines)56     public WakelockItem parse(List<String> lines) {
57         Matcher m = null;
58         int wakelockCounter = 0;
59         for (String line : lines) {
60             if (wakelockCounter >= TOP_WAKELOCK_COUNT || "".equals(line.trim())) {
61                 // Done with wakelock parsing
62                 break;
63             }
64 
65             m = KERNEL_WAKE_LOCK_PAT.matcher(line);
66             if (m.matches() && !line.contains("PowerManagerService.WakeLocks")) {
67                 parseKernelWakeLock(line, WakeLockCategory.KERNEL_WAKELOCK);
68                 wakelockCounter++;
69                 continue;
70             }
71 
72             m = PARTIAL_WAKE_LOCK_PAT.matcher(line);
73             if (m.matches()) {
74                 parsePartialWakeLock(line, WakeLockCategory.PARTIAL_WAKELOCK);
75                 wakelockCounter++;
76             }
77         }
78 
79         return mItem;
80     }
81 
82     /**
83      * Parse a line of output and add it to wakelock section
84      * <p>
85      * Exposed for unit testing.
86      * </p>
87      */
parseKernelWakeLock(String line, WakeLockCategory category)88     void parseKernelWakeLock(String line, WakeLockCategory category) {
89         Matcher m = KERNEL_WAKE_LOCK_PAT.matcher(line);
90         if (!m.matches()) {
91             return;
92         }
93         final String name = m.group(1);
94         final long wakelockTime = NumberFormattingUtil.getMs(
95                 NumberFormattingUtil.parseIntOrZero(m.group(2)),
96                 NumberFormattingUtil.parseIntOrZero(m.group(3)),
97                 NumberFormattingUtil.parseIntOrZero(m.group(4)),
98                 NumberFormattingUtil.parseIntOrZero(m.group(5)),
99                 NumberFormattingUtil.parseIntOrZero(m.group(6)));
100 
101         final int timesCalled = Integer.parseInt(m.group(7));
102 
103         mItem.addWakeLock(name, wakelockTime, timesCalled, category);
104     }
105 
106     /**
107      * Parse a line of output and add it to wake lock section.
108      * <p>
109      * Exposed for unit testing.
110      * </p>
111      */
parsePartialWakeLock(String line, WakeLockCategory category)112     void parsePartialWakeLock(String line, WakeLockCategory category) {
113         Matcher m = PARTIAL_WAKE_LOCK_PAT.matcher(line);
114         if (!m.matches()) {
115             return;
116         }
117         final String processUID = m.group(1);
118         final String name = m.group(2);
119         final long wakelockTime = NumberFormattingUtil.getMs(
120                 NumberFormattingUtil.parseIntOrZero(m.group(3)),
121                 NumberFormattingUtil.parseIntOrZero(m.group(4)),
122                 NumberFormattingUtil.parseIntOrZero(m.group(5)),
123                 NumberFormattingUtil.parseIntOrZero(m.group(6)),
124                 NumberFormattingUtil.parseIntOrZero(m.group(7)));
125         final int timesCalled = Integer.parseInt(m.group(8));
126 
127         mItem.addWakeLock(name, processUID, wakelockTime, timesCalled, category);
128     }
129 
130     /**
131      * Get the {@link WakelockItem}.
132      * <p>
133      * Exposed for unit testing.
134      * </p>
135      */
getItem()136     WakelockItem getItem() {
137         return mItem;
138     }
139 }
140