Home | History | Annotate | Download | only in art
      1 /*
      2  * Copyright (C) 2017 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 art;
     18 
     19 import java.lang.reflect.Executable;
     20 import java.util.HashSet;
     21 import java.util.Set;
     22 import java.util.Objects;
     23 
     24 public class Breakpoint {
     25   public static class Manager {
     26     public static class BP {
     27       public final Executable method;
     28       public final long location;
     29 
     30       public BP(Executable method) {
     31         this(method, getStartLocation(method));
     32       }
     33 
     34       public BP(Executable method, long location) {
     35         this.method = method;
     36         this.location = location;
     37       }
     38 
     39       @Override
     40       public boolean equals(Object other) {
     41         return (other instanceof BP) &&
     42             method.equals(((BP)other).method) &&
     43             location == ((BP)other).location;
     44       }
     45 
     46       @Override
     47       public String toString() {
     48         return method.toString() + " @ " + getLine();
     49       }
     50 
     51       @Override
     52       public int hashCode() {
     53         return Objects.hash(method, location);
     54       }
     55 
     56       public int getLine() {
     57         try {
     58           LineNumber[] lines = getLineNumberTable(method);
     59           int best = -1;
     60           for (LineNumber l : lines) {
     61             if (l.location > location) {
     62               break;
     63             } else {
     64               best = l.line;
     65             }
     66           }
     67           return best;
     68         } catch (Exception e) {
     69           return -1;
     70         }
     71       }
     72     }
     73 
     74     private Set<BP> breaks = new HashSet<>();
     75 
     76     public void setBreakpoints(BP... bs) {
     77       for (BP b : bs) {
     78         if (breaks.add(b)) {
     79           Breakpoint.setBreakpoint(b.method, b.location);
     80         }
     81       }
     82     }
     83     public void setBreakpoint(Executable method, long location) {
     84       setBreakpoints(new BP(method, location));
     85     }
     86 
     87     public void clearBreakpoints(BP... bs) {
     88       for (BP b : bs) {
     89         if (breaks.remove(b)) {
     90           Breakpoint.clearBreakpoint(b.method, b.location);
     91         }
     92       }
     93     }
     94     public void clearBreakpoint(Executable method, long location) {
     95       clearBreakpoints(new BP(method, location));
     96     }
     97 
     98     public void clearAllBreakpoints() {
     99       clearBreakpoints(breaks.toArray(new BP[0]));
    100     }
    101   }
    102 
    103   public static void startBreakpointWatch(Class<?> methodClass,
    104                                           Executable breakpointReached,
    105                                           Thread thr) {
    106     startBreakpointWatch(methodClass, breakpointReached, false, thr);
    107   }
    108 
    109   /**
    110    * Enables the trapping of breakpoint events.
    111    *
    112    * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
    113    */
    114   public static native void startBreakpointWatch(Class<?> methodClass,
    115                                                  Executable breakpointReached,
    116                                                  boolean allowRecursive,
    117                                                  Thread thr);
    118   public static native void stopBreakpointWatch(Thread thr);
    119 
    120   public static final class LineNumber implements Comparable<LineNumber> {
    121     public final long location;
    122     public final int line;
    123 
    124     private LineNumber(long loc, int line) {
    125       this.location = loc;
    126       this.line = line;
    127     }
    128 
    129     public boolean equals(Object other) {
    130       return other instanceof LineNumber && ((LineNumber)other).line == line &&
    131           ((LineNumber)other).location == location;
    132     }
    133 
    134     public int compareTo(LineNumber other) {
    135       int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
    136       if (v != 0) {
    137         return v;
    138       } else {
    139         return Long.valueOf(location).compareTo(Long.valueOf(other.location));
    140       }
    141     }
    142   }
    143 
    144   public static native void setBreakpoint(Executable m, long loc);
    145   public static void setBreakpoint(Executable m, LineNumber l) {
    146     setBreakpoint(m, l.location);
    147   }
    148 
    149   public static native void clearBreakpoint(Executable m, long loc);
    150   public static void clearBreakpoint(Executable m, LineNumber l) {
    151     clearBreakpoint(m, l.location);
    152   }
    153 
    154   private static native Object[] getLineNumberTableNative(Executable m);
    155   public static LineNumber[] getLineNumberTable(Executable m) {
    156     Object[] nativeTable = getLineNumberTableNative(m);
    157     long[] location = (long[])(nativeTable[0]);
    158     int[] lines = (int[])(nativeTable[1]);
    159     if (lines.length != location.length) {
    160       throw new Error("Lines and locations have different lengths!");
    161     }
    162     LineNumber[] out = new LineNumber[lines.length];
    163     for (int i = 0; i < lines.length; i++) {
    164       out[i] = new LineNumber(location[i], lines[i]);
    165     }
    166     return out;
    167   }
    168 
    169   public static native long getStartLocation(Executable m);
    170 
    171   public static int locationToLine(Executable m, long location) {
    172     try {
    173       Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
    174       int best = -1;
    175       for (Breakpoint.LineNumber l : lines) {
    176         if (l.location > location) {
    177           break;
    178         } else {
    179           best = l.line;
    180         }
    181       }
    182       return best;
    183     } catch (Exception e) {
    184       return -1;
    185     }
    186   }
    187 
    188   public static long lineToLocation(Executable m, int line) throws Exception {
    189     try {
    190       Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
    191       for (Breakpoint.LineNumber l : lines) {
    192         if (l.line == line) {
    193           return l.location;
    194         }
    195       }
    196       throw new Exception("Unable to find line " + line + " in " + m);
    197     } catch (Exception e) {
    198       throw new Exception("Unable to get line number info for " + m, e);
    199     }
    200   }
    201 }
    202 
    203