1 /*
2  * Copyright (C) 2016 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 
17 package com.android.bugreport.stacks;
18 
19 import com.android.bugreport.util.Line;
20 import com.android.bugreport.util.Lines;
21 import com.android.bugreport.util.Utils;
22 
23 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.regex.Pattern;
27 import java.util.regex.Matcher;
28 
29 /**
30  * Parse a vm traces thread.
31  *
32  * The parser can be reused, but is not thread safe.
33  */
34 public class ThreadSnapshotParser {
35     public static final Pattern BEGIN_UNMANAGED_THREAD_RE = Pattern.compile(
36                     "\"(.*)\" sysTid=(\\d+)(.*)");
37     public static final Pattern BEGIN_MANAGED_THREAD_RE = Pattern.compile(
38                     "\"(.*)\" (.*) ?prio=(\\d+)\\s+tid=(\\d+)\\s*(.*)");
39     public static final Pattern BEGIN_NOT_ATTACHED_THREAD_RE = Pattern.compile(
40                     "\"(.*)\" (.*) ?prio=(\\d+)\\s+(\\(not attached\\))");
41 
42     public static final Pattern ATTR_RE = Pattern.compile(
43                     "  \\| (.*)");
44     public static final Pattern HELD_MUTEXES_RE = Pattern.compile(
45                     "  \\| (held mutexes=\\s*(.*))");
46     public static final Pattern NATIVE_RE = Pattern.compile(
47                     "  (?:native: )?#\\d+ \\S+ [0-9a-fA-F]+\\s+(.*)\\s+\\((.*)\\+(\\d+)\\)");
48     public static final Pattern NATIVE_NO_LOC_RE = Pattern.compile(
49                     "  (?:native: )?#\\d+ \\S+ [0-9a-fA-F]+\\s+(.*)\\s*\\(?(.*)\\)?");
50     public static final Pattern KERNEL_RE = Pattern.compile(
51                     "  kernel: (.*)\\+0x([0-9a-fA-F]+)/0x([0-9a-fA-F]+)");
52     public static final Pattern KERNEL_UNKNOWN_RE = Pattern.compile(
53                     "  kernel: \\(couldn't read /proc/self/task/\\d+/stack\\)");
54     public static final Pattern JAVA_RE = Pattern.compile(
55                     "  at (?:(.+)\\.)?([^.]+)\\.([^.]+)\\((.*):([\\d-]+)\\)");
56     public static final Pattern JNI_RE = Pattern.compile(
57                     "  at (?:(.+)\\.)?([^.]+)\\.([^.]+)\\(Native method\\)");
58     public static final Pattern LOCKED_RE = Pattern.compile(
59                     "  - locked \\<0x([0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
60     public static final Pattern SLEEPING_ON_RE = Pattern.compile(
61                     "  - sleeping on \\<0x([0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
62     public static final Pattern WAITING_ON_RE = Pattern.compile(
63                     "  - waiting on \\<0x([0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
64     public static final Pattern WAITING_TO_LOCK_RE = Pattern.compile(
65                     "  - waiting to lock \\<0x([0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
66     public static final Pattern WAITING_TO_LOCK_HELD_RE = Pattern.compile(
67                     "  - waiting to lock \\<0x([0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)"
68                     + "(?: held by thread (\\d+))");
69     public static final Pattern WAITING_TO_LOCK_UNKNOWN_RE = Pattern.compile(
70                     "  - waiting to lock an unknown object");
71     public static final Pattern NO_MANAGED_STACK_FRAME_RE = Pattern.compile(
72                     "  (\\(no managed stack frames\\))");
73     private static final Pattern BLANK_RE
74             = Pattern.compile("\\s+");
75 
76     public static final Pattern SYS_TID_ATTR_RE = Pattern.compile(
77                     "  \\| sysTid=(\\d+) .*");
78     public static final Pattern STATE_ATTR_RE = Pattern.compile(
79                     "  \\| state=R .*");
80 
81     /**
82      * Construct a new parser.
83      */
ThreadSnapshotParser()84     public ThreadSnapshotParser() {
85     }
86 
87     /**
88      * Parse the given Lines until the first blank line, which signals the
89      * end of the thread. Return a ThreadSnapshot object or null if there wasn't
90      * enough text to parse.
91      */
parse(Lines<? extends Line> lines)92     public ThreadSnapshot parse(Lines<? extends Line> lines) {
93         final ThreadSnapshot result = new ThreadSnapshot();
94         JavaStackFrameSnapshot lastJava = null;
95 
96         final Matcher beginUnmanagedThreadRe = BEGIN_UNMANAGED_THREAD_RE.matcher("");
97         final Matcher beginManagedThreadRe = BEGIN_MANAGED_THREAD_RE.matcher("");
98         final Matcher beginNotAttachedThreadRe = BEGIN_NOT_ATTACHED_THREAD_RE.matcher("");
99         final Matcher attrRe = ATTR_RE.matcher("");
100         final Matcher heldMutexesRe = HELD_MUTEXES_RE.matcher("");
101         final Matcher nativeRe = NATIVE_RE.matcher("");
102         final Matcher nativeNoLocRe = NATIVE_NO_LOC_RE.matcher("");
103         final Matcher kernelRe = KERNEL_RE.matcher("");
104         final Matcher kernelUnknownRe = KERNEL_UNKNOWN_RE.matcher("");
105         final Matcher javaRe = JAVA_RE.matcher("");
106         final Matcher jniRe = JNI_RE.matcher("");
107         final Matcher lockedRe = LOCKED_RE.matcher("");
108         final Matcher waitingOnRe = WAITING_ON_RE.matcher("");
109         final Matcher sleepingOnRe = SLEEPING_ON_RE.matcher("");
110         final Matcher waitingToLockHeldRe = WAITING_TO_LOCK_HELD_RE.matcher("");
111         final Matcher waitingToLockRe = WAITING_TO_LOCK_RE.matcher("");
112         final Matcher waitingToLockUnknownRe = WAITING_TO_LOCK_UNKNOWN_RE.matcher("");
113         final Matcher noManagedStackFrameRe = NO_MANAGED_STACK_FRAME_RE.matcher("");
114         final Matcher blankRe = BLANK_RE.matcher("");
115 
116         final Matcher sysTidAttrRe = SYS_TID_ATTR_RE.matcher("");
117         final Matcher stateAttrRe = STATE_ATTR_RE.matcher("");
118 
119 
120         Line line;
121         String text;
122 
123         // First Line
124         if (!lines.hasNext()) {
125             // TODO: Handle errors.
126             return null;
127         }
128         line = lines.next();
129         if (Utils.matches(beginUnmanagedThreadRe, line.text)) {
130             result.type = ThreadSnapshot.TYPE_UNMANAGED;
131             result.name = beginUnmanagedThreadRe.group(1);
132             result.priority = -1;
133             result.tid = -1;
134             result.sysTid = Integer.parseInt(beginUnmanagedThreadRe.group(2));
135         } else if (Utils.matches(beginManagedThreadRe, line.text)) {
136             result.type = ThreadSnapshot.TYPE_MANAGED;
137             result.name = beginManagedThreadRe.group(1);
138             result.daemon = beginManagedThreadRe.group(2);
139             result.priority = Utils.getInt(beginManagedThreadRe, 3, -1);
140             result.tid = Utils.getInt(beginManagedThreadRe, 4, -1);
141             result.vmState = beginManagedThreadRe.group(5);
142         } else if (Utils.matches(beginNotAttachedThreadRe, line.text)) {
143             result.type = ThreadSnapshot.TYPE_MANAGED;
144             result.name = beginNotAttachedThreadRe.group(1);
145             result.daemon = beginNotAttachedThreadRe.group(2);
146             result.priority = Utils.getInt(beginNotAttachedThreadRe, 3, -1);
147             result.tid = -1;
148             result.vmState = beginNotAttachedThreadRe.group(4);
149         }
150 
151         // Attributes
152         while (lines.hasNext()) {
153             line = lines.next();
154             text = line.text;
155             if (Utils.matches(heldMutexesRe, text)) {
156                 result.attributeText.add(heldMutexesRe.group(1));
157                 result.heldMutexes = heldMutexesRe.group(2);
158             } else if (Utils.matches(attrRe, text)) {
159                 result.attributeText.add(attrRe.group(1));
160                 if (Utils.matches(sysTidAttrRe, text)) {
161                     result.sysTid = Integer.parseInt(sysTidAttrRe.group(1));
162                 }
163                 if (Utils.matches(stateAttrRe, text)) {
164                     result.runnable = true;
165                 }
166             } else {
167                 lines.rewind();
168                 break;
169             }
170         }
171 
172         // Stack
173         while (lines.hasNext()) {
174             line = lines.next();
175             text = line.text;
176             if (Utils.matches(nativeRe, text)) {
177                 final NativeStackFrameSnapshot frame = new NativeStackFrameSnapshot();
178                 frame.text = text;
179                 frame.library = nativeRe.group(1);
180                 frame.symbol = nativeRe.group(2);
181                 frame.offset = Integer.parseInt(nativeRe.group(3));
182                 result.frames.add(frame);
183                 lastJava = null;
184             } else if (Utils.matches(nativeNoLocRe, text)) {
185                 final NativeStackFrameSnapshot frame = new NativeStackFrameSnapshot();
186                 frame.text = text;
187                 frame.library = nativeNoLocRe.group(1);
188                 frame.symbol = nativeNoLocRe.group(2);
189                 frame.offset = -1;
190                 result.frames.add(frame);
191                 lastJava = null;
192             } else if (Utils.matches(kernelRe, text)) {
193                 final KernelStackFrameSnapshot frame = new KernelStackFrameSnapshot();
194                 frame.text = text;
195                 frame.syscall = kernelRe.group(1);
196                 frame.offset0 = Integer.parseInt(kernelRe.group(3), 16);
197                 frame.offset1 = Integer.parseInt(kernelRe.group(3), 16);
198                 result.frames.add(frame);
199                 lastJava = null;
200             } else if (Utils.matches(kernelUnknownRe, text)) {
201                 final StackFrameSnapshot frame = new StackFrameSnapshot();
202                 frame.text = text;
203                 result.frames.add(frame);
204                 lastJava = null;
205             } else if (Utils.matches(javaRe, text)) {
206                 final JavaStackFrameSnapshot frame = new JavaStackFrameSnapshot();
207                 frame.text = text;
208                 frame.packageName = javaRe.group(1);
209                 frame.className = javaRe.group(2);
210                 frame.methodName = javaRe.group(3);
211                 frame.sourceFile = javaRe.group(4);
212                 frame.sourceLine = Integer.parseInt(javaRe.group(5));
213                 frame.language = JavaStackFrameSnapshot.LANGUAGE_JAVA;
214                 result.frames.add(frame);
215                 lastJava = frame;
216             } else if (Utils.matches(jniRe, text)) {
217                 final JavaStackFrameSnapshot frame = new JavaStackFrameSnapshot();
218                 frame.text = text;
219                 frame.packageName = jniRe.group(1);
220                 frame.className = jniRe.group(2);
221                 frame.methodName = jniRe.group(3);
222                 frame.language = JavaStackFrameSnapshot.LANGUAGE_JNI;
223                 result.frames.add(frame);
224                 lastJava = frame;
225             } else if (Utils.matches(lockedRe, text)) {
226                 if (lastJava != null) {
227                     final LockSnapshot lock = new LockSnapshot();
228                     lock.type = LockSnapshot.LOCKED;
229                     lock.address = lockedRe.group(1);
230                     lock.packageName = lockedRe.group(2);
231                     lock.className = lockedRe.group(3);
232                     lastJava.locks.add(lock);
233                 }
234             } else if (Utils.matches(waitingOnRe, text)) {
235                 if (lastJava != null) {
236                     final LockSnapshot lock = new LockSnapshot();
237                     lock.type = LockSnapshot.WAITING;
238                     lock.address = waitingOnRe.group(1);
239                     lock.packageName = waitingOnRe.group(2);
240                     lock.className = waitingOnRe.group(3);
241                     lastJava.locks.add(lock);
242                 }
243             } else if (Utils.matches(sleepingOnRe, text)) {
244                 if (lastJava != null) {
245                     final LockSnapshot lock = new LockSnapshot();
246                     lock.type = LockSnapshot.SLEEPING;
247                     lock.address = sleepingOnRe.group(1);
248                     lock.packageName = sleepingOnRe.group(2);
249                     lock.className = sleepingOnRe.group(3);
250                     lastJava.locks.add(lock);
251                 }
252             } else if (Utils.matches(waitingToLockHeldRe, text)) {
253                 if (lastJava != null) {
254                     final LockSnapshot lock = new LockSnapshot();
255                     lock.type = LockSnapshot.BLOCKED;
256                     lock.address = waitingToLockHeldRe.group(1);
257                     lock.packageName = waitingToLockHeldRe.group(2);
258                     lock.className = waitingToLockHeldRe.group(3);
259                     lock.threadId = Integer.parseInt(waitingToLockHeldRe.group(4));
260                     lastJava.locks.add(lock);
261                 }
262             } else if (Utils.matches(waitingToLockRe, text)) {
263                 if (lastJava != null) {
264                     final LockSnapshot lock = new LockSnapshot();
265                     lock.type = LockSnapshot.BLOCKED;
266                     lock.address = waitingToLockRe.group(1);
267                     lock.packageName = waitingToLockRe.group(2);
268                     lock.className = waitingToLockRe.group(3);
269                     lock.threadId = -1;
270                     lastJava.locks.add(lock);
271                 }
272             } else if (Utils.matches(waitingToLockUnknownRe, text)) {
273                 if (lastJava != null) {
274                     final LockSnapshot lock = new LockSnapshot();
275                     lock.type = LockSnapshot.BLOCKED;
276                     lastJava.locks.add(lock);
277                 }
278             } else if (Utils.matches(noManagedStackFrameRe, text)) {
279                 final StackFrameSnapshot frame = new StackFrameSnapshot();
280                 frame.text = noManagedStackFrameRe.group(1);
281                 result.frames.add(frame);
282                 lastJava = null;
283             } else if (text.length() == 0 || Utils.matches(blankRe, text)) {
284                 break;
285             } else {
286                 final StackFrameSnapshot frame = new StackFrameSnapshot();
287                 frame.text = text;
288                 result.frames.add(frame);
289                 lastJava = null;
290                 System.out.println("  other  ==> [" + frame.text + "]");
291             }
292         }
293 
294 
295         if (false) {
296             System.out.println();
297             System.out.println("THREAD");
298             System.out.println("name=" + result.name);
299             System.out.println("daemon=" + result.daemon);
300             System.out.println("priority=" + result.priority);
301             System.out.println("tid=" + result.tid);
302             System.out.println("vmState=" + result.vmState);
303             for (String s: result.attributeText) {
304                 System.out.println("  attr --> " + s);
305             }
306             System.out.println("heldMutexes=" + result.heldMutexes);
307             for (StackFrameSnapshot frame: result.frames) {
308                 if (frame.frameType == StackFrameSnapshot.FRAME_TYPE_NATIVE) {
309                     final NativeStackFrameSnapshot nf = (NativeStackFrameSnapshot)frame;
310                     System.out.println("  frame(native) ==> " + nf.library
311                             + " / " + nf.symbol + " / " + nf.offset);
312                 } else if (frame.frameType == StackFrameSnapshot.FRAME_TYPE_KERNEL) {
313                     final KernelStackFrameSnapshot kf = (KernelStackFrameSnapshot)frame;
314                     System.out.println("  frame(kernel) ==> " + kf.syscall
315                             + " / 0x" + kf.offset0 + " / 0x" + kf.offset1);
316                 } else if (frame.frameType == StackFrameSnapshot.FRAME_TYPE_JAVA) {
317                     final JavaStackFrameSnapshot jf = (JavaStackFrameSnapshot)frame;
318                     System.out.println("  frame(java)   ==> " + jf.packageName
319                             + " / " + jf.className + " / " + jf.methodName
320                             + " / " + jf.sourceFile + " / " + jf.sourceLine
321                             + " / "
322                             + (jf.language == JavaStackFrameSnapshot.LANGUAGE_JAVA ? "java" : "jni")
323                             + " ===> " + jf.text);
324                     for (LockSnapshot ls: jf.locks) {
325                         System.out.println("                --> "
326                                 + (ls.type == LockSnapshot.LOCKED ? "locked" : "waiting")
327                                 + " / " + ls.address + " / " + ls.packageName
328                                 + " / " + ls.className);
329                     }
330                 } else {
331                     System.out.println("  frame(other)  ==> " + frame.text);
332                 }
333             }
334         }
335 
336         return result;
337     }
338 }
339 
340