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