Home | History | Annotate | Download | only in stacks
      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      */
     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      */
     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