1*795d594fSAndroid Build Coastguard Worker /* 2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2017 The Android Open Source Project 3*795d594fSAndroid Build Coastguard Worker * 4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*795d594fSAndroid Build Coastguard Worker * 8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*795d594fSAndroid Build Coastguard Worker * 10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*795d594fSAndroid Build Coastguard Worker * limitations under the License. 15*795d594fSAndroid Build Coastguard Worker */ 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Worker package art; 18*795d594fSAndroid Build Coastguard Worker 19*795d594fSAndroid Build Coastguard Worker import java.lang.reflect.Executable; 20*795d594fSAndroid Build Coastguard Worker import java.util.HashSet; 21*795d594fSAndroid Build Coastguard Worker import java.util.Set; 22*795d594fSAndroid Build Coastguard Worker import java.util.Objects; 23*795d594fSAndroid Build Coastguard Worker 24*795d594fSAndroid Build Coastguard Worker public class Breakpoint { 25*795d594fSAndroid Build Coastguard Worker public static class Manager { 26*795d594fSAndroid Build Coastguard Worker public static class BP { 27*795d594fSAndroid Build Coastguard Worker public final Executable method; 28*795d594fSAndroid Build Coastguard Worker public final long location; 29*795d594fSAndroid Build Coastguard Worker BP(Executable method)30*795d594fSAndroid Build Coastguard Worker public BP(Executable method) { 31*795d594fSAndroid Build Coastguard Worker this(method, getStartLocation(method)); 32*795d594fSAndroid Build Coastguard Worker } 33*795d594fSAndroid Build Coastguard Worker BP(Executable method, long location)34*795d594fSAndroid Build Coastguard Worker public BP(Executable method, long location) { 35*795d594fSAndroid Build Coastguard Worker this.method = method; 36*795d594fSAndroid Build Coastguard Worker this.location = location; 37*795d594fSAndroid Build Coastguard Worker } 38*795d594fSAndroid Build Coastguard Worker 39*795d594fSAndroid Build Coastguard Worker @Override equals(Object other)40*795d594fSAndroid Build Coastguard Worker public boolean equals(Object other) { 41*795d594fSAndroid Build Coastguard Worker return (other instanceof BP) && 42*795d594fSAndroid Build Coastguard Worker method.equals(((BP)other).method) && 43*795d594fSAndroid Build Coastguard Worker location == ((BP)other).location; 44*795d594fSAndroid Build Coastguard Worker } 45*795d594fSAndroid Build Coastguard Worker 46*795d594fSAndroid Build Coastguard Worker @Override toString()47*795d594fSAndroid Build Coastguard Worker public String toString() { 48*795d594fSAndroid Build Coastguard Worker return method.toString() + " @ " + getLine(); 49*795d594fSAndroid Build Coastguard Worker } 50*795d594fSAndroid Build Coastguard Worker 51*795d594fSAndroid Build Coastguard Worker @Override hashCode()52*795d594fSAndroid Build Coastguard Worker public int hashCode() { 53*795d594fSAndroid Build Coastguard Worker return Objects.hash(method, location); 54*795d594fSAndroid Build Coastguard Worker } 55*795d594fSAndroid Build Coastguard Worker getLine()56*795d594fSAndroid Build Coastguard Worker public int getLine() { 57*795d594fSAndroid Build Coastguard Worker try { 58*795d594fSAndroid Build Coastguard Worker LineNumber[] lines = getLineNumberTable(method); 59*795d594fSAndroid Build Coastguard Worker int best = -1; 60*795d594fSAndroid Build Coastguard Worker for (LineNumber l : lines) { 61*795d594fSAndroid Build Coastguard Worker if (l.location > location) { 62*795d594fSAndroid Build Coastguard Worker break; 63*795d594fSAndroid Build Coastguard Worker } else { 64*795d594fSAndroid Build Coastguard Worker best = l.line; 65*795d594fSAndroid Build Coastguard Worker } 66*795d594fSAndroid Build Coastguard Worker } 67*795d594fSAndroid Build Coastguard Worker return best; 68*795d594fSAndroid Build Coastguard Worker } catch (Exception e) { 69*795d594fSAndroid Build Coastguard Worker return -1; 70*795d594fSAndroid Build Coastguard Worker } 71*795d594fSAndroid Build Coastguard Worker } 72*795d594fSAndroid Build Coastguard Worker } 73*795d594fSAndroid Build Coastguard Worker 74*795d594fSAndroid Build Coastguard Worker private Set<BP> breaks = new HashSet<>(); 75*795d594fSAndroid Build Coastguard Worker setBreakpoints(BP... bs)76*795d594fSAndroid Build Coastguard Worker public void setBreakpoints(BP... bs) { 77*795d594fSAndroid Build Coastguard Worker for (BP b : bs) { 78*795d594fSAndroid Build Coastguard Worker if (breaks.add(b)) { 79*795d594fSAndroid Build Coastguard Worker Breakpoint.setBreakpoint(b.method, b.location); 80*795d594fSAndroid Build Coastguard Worker } 81*795d594fSAndroid Build Coastguard Worker } 82*795d594fSAndroid Build Coastguard Worker } setBreakpoint(Executable method, long location)83*795d594fSAndroid Build Coastguard Worker public void setBreakpoint(Executable method, long location) { 84*795d594fSAndroid Build Coastguard Worker setBreakpoints(new BP(method, location)); 85*795d594fSAndroid Build Coastguard Worker } 86*795d594fSAndroid Build Coastguard Worker clearBreakpoints(BP... bs)87*795d594fSAndroid Build Coastguard Worker public void clearBreakpoints(BP... bs) { 88*795d594fSAndroid Build Coastguard Worker for (BP b : bs) { 89*795d594fSAndroid Build Coastguard Worker if (breaks.remove(b)) { 90*795d594fSAndroid Build Coastguard Worker Breakpoint.clearBreakpoint(b.method, b.location); 91*795d594fSAndroid Build Coastguard Worker } 92*795d594fSAndroid Build Coastguard Worker } 93*795d594fSAndroid Build Coastguard Worker } clearBreakpoint(Executable method, long location)94*795d594fSAndroid Build Coastguard Worker public void clearBreakpoint(Executable method, long location) { 95*795d594fSAndroid Build Coastguard Worker clearBreakpoints(new BP(method, location)); 96*795d594fSAndroid Build Coastguard Worker } 97*795d594fSAndroid Build Coastguard Worker clearAllBreakpoints()98*795d594fSAndroid Build Coastguard Worker public void clearAllBreakpoints() { 99*795d594fSAndroid Build Coastguard Worker clearBreakpoints(breaks.toArray(new BP[0])); 100*795d594fSAndroid Build Coastguard Worker } 101*795d594fSAndroid Build Coastguard Worker } 102*795d594fSAndroid Build Coastguard Worker startBreakpointWatch(Class<?> methodClass, Executable breakpointReached, Thread thr)103*795d594fSAndroid Build Coastguard Worker public static void startBreakpointWatch(Class<?> methodClass, 104*795d594fSAndroid Build Coastguard Worker Executable breakpointReached, 105*795d594fSAndroid Build Coastguard Worker Thread thr) { 106*795d594fSAndroid Build Coastguard Worker startBreakpointWatch(methodClass, breakpointReached, false, thr); 107*795d594fSAndroid Build Coastguard Worker } 108*795d594fSAndroid Build Coastguard Worker 109*795d594fSAndroid Build Coastguard Worker /** 110*795d594fSAndroid Build Coastguard Worker * Enables the trapping of breakpoint events. 111*795d594fSAndroid Build Coastguard Worker * 112*795d594fSAndroid Build Coastguard Worker * If allowRecursive == true then breakpoints will be sent even if one is currently being handled. 113*795d594fSAndroid Build Coastguard Worker */ startBreakpointWatch(Class<?> methodClass, Executable breakpointReached, boolean allowRecursive, Thread thr)114*795d594fSAndroid Build Coastguard Worker public static native void startBreakpointWatch(Class<?> methodClass, 115*795d594fSAndroid Build Coastguard Worker Executable breakpointReached, 116*795d594fSAndroid Build Coastguard Worker boolean allowRecursive, 117*795d594fSAndroid Build Coastguard Worker Thread thr); stopBreakpointWatch(Thread thr)118*795d594fSAndroid Build Coastguard Worker public static native void stopBreakpointWatch(Thread thr); 119*795d594fSAndroid Build Coastguard Worker 120*795d594fSAndroid Build Coastguard Worker public static final class LineNumber implements Comparable<LineNumber> { 121*795d594fSAndroid Build Coastguard Worker public final long location; 122*795d594fSAndroid Build Coastguard Worker public final int line; 123*795d594fSAndroid Build Coastguard Worker LineNumber(long loc, int line)124*795d594fSAndroid Build Coastguard Worker private LineNumber(long loc, int line) { 125*795d594fSAndroid Build Coastguard Worker this.location = loc; 126*795d594fSAndroid Build Coastguard Worker this.line = line; 127*795d594fSAndroid Build Coastguard Worker } 128*795d594fSAndroid Build Coastguard Worker equals(Object other)129*795d594fSAndroid Build Coastguard Worker public boolean equals(Object other) { 130*795d594fSAndroid Build Coastguard Worker return other instanceof LineNumber && ((LineNumber)other).line == line && 131*795d594fSAndroid Build Coastguard Worker ((LineNumber)other).location == location; 132*795d594fSAndroid Build Coastguard Worker } 133*795d594fSAndroid Build Coastguard Worker compareTo(LineNumber other)134*795d594fSAndroid Build Coastguard Worker public int compareTo(LineNumber other) { 135*795d594fSAndroid Build Coastguard Worker int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line)); 136*795d594fSAndroid Build Coastguard Worker if (v != 0) { 137*795d594fSAndroid Build Coastguard Worker return v; 138*795d594fSAndroid Build Coastguard Worker } else { 139*795d594fSAndroid Build Coastguard Worker return Long.valueOf(location).compareTo(Long.valueOf(other.location)); 140*795d594fSAndroid Build Coastguard Worker } 141*795d594fSAndroid Build Coastguard Worker } 142*795d594fSAndroid Build Coastguard Worker } 143*795d594fSAndroid Build Coastguard Worker setBreakpoint(Executable m, long loc)144*795d594fSAndroid Build Coastguard Worker public static native void setBreakpoint(Executable m, long loc); setBreakpoint(Executable m, LineNumber l)145*795d594fSAndroid Build Coastguard Worker public static void setBreakpoint(Executable m, LineNumber l) { 146*795d594fSAndroid Build Coastguard Worker setBreakpoint(m, l.location); 147*795d594fSAndroid Build Coastguard Worker } 148*795d594fSAndroid Build Coastguard Worker clearBreakpoint(Executable m, long loc)149*795d594fSAndroid Build Coastguard Worker public static native void clearBreakpoint(Executable m, long loc); clearBreakpoint(Executable m, LineNumber l)150*795d594fSAndroid Build Coastguard Worker public static void clearBreakpoint(Executable m, LineNumber l) { 151*795d594fSAndroid Build Coastguard Worker clearBreakpoint(m, l.location); 152*795d594fSAndroid Build Coastguard Worker } 153*795d594fSAndroid Build Coastguard Worker getLineNumberTableNative(Executable m)154*795d594fSAndroid Build Coastguard Worker private static native Object[] getLineNumberTableNative(Executable m); getLineNumberTable(Executable m)155*795d594fSAndroid Build Coastguard Worker public static LineNumber[] getLineNumberTable(Executable m) { 156*795d594fSAndroid Build Coastguard Worker Object[] nativeTable = getLineNumberTableNative(m); 157*795d594fSAndroid Build Coastguard Worker long[] location = (long[])(nativeTable[0]); 158*795d594fSAndroid Build Coastguard Worker int[] lines = (int[])(nativeTable[1]); 159*795d594fSAndroid Build Coastguard Worker if (lines.length != location.length) { 160*795d594fSAndroid Build Coastguard Worker throw new Error("Lines and locations have different lengths!"); 161*795d594fSAndroid Build Coastguard Worker } 162*795d594fSAndroid Build Coastguard Worker LineNumber[] out = new LineNumber[lines.length]; 163*795d594fSAndroid Build Coastguard Worker for (int i = 0; i < lines.length; i++) { 164*795d594fSAndroid Build Coastguard Worker out[i] = new LineNumber(location[i], lines[i]); 165*795d594fSAndroid Build Coastguard Worker } 166*795d594fSAndroid Build Coastguard Worker return out; 167*795d594fSAndroid Build Coastguard Worker } 168*795d594fSAndroid Build Coastguard Worker getStartLocation(Executable m)169*795d594fSAndroid Build Coastguard Worker public static native long getStartLocation(Executable m); 170*795d594fSAndroid Build Coastguard Worker locationToLine(Executable m, long location)171*795d594fSAndroid Build Coastguard Worker public static int locationToLine(Executable m, long location) { 172*795d594fSAndroid Build Coastguard Worker try { 173*795d594fSAndroid Build Coastguard Worker Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); 174*795d594fSAndroid Build Coastguard Worker int best = -1; 175*795d594fSAndroid Build Coastguard Worker for (Breakpoint.LineNumber l : lines) { 176*795d594fSAndroid Build Coastguard Worker if (l.location > location) { 177*795d594fSAndroid Build Coastguard Worker break; 178*795d594fSAndroid Build Coastguard Worker } else { 179*795d594fSAndroid Build Coastguard Worker best = l.line; 180*795d594fSAndroid Build Coastguard Worker } 181*795d594fSAndroid Build Coastguard Worker } 182*795d594fSAndroid Build Coastguard Worker return best; 183*795d594fSAndroid Build Coastguard Worker } catch (Exception e) { 184*795d594fSAndroid Build Coastguard Worker return -1; 185*795d594fSAndroid Build Coastguard Worker } 186*795d594fSAndroid Build Coastguard Worker } 187*795d594fSAndroid Build Coastguard Worker lineToLocation(Executable m, int line)188*795d594fSAndroid Build Coastguard Worker public static long lineToLocation(Executable m, int line) throws Exception { 189*795d594fSAndroid Build Coastguard Worker try { 190*795d594fSAndroid Build Coastguard Worker Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); 191*795d594fSAndroid Build Coastguard Worker for (Breakpoint.LineNumber l : lines) { 192*795d594fSAndroid Build Coastguard Worker if (l.line == line) { 193*795d594fSAndroid Build Coastguard Worker return l.location; 194*795d594fSAndroid Build Coastguard Worker } 195*795d594fSAndroid Build Coastguard Worker } 196*795d594fSAndroid Build Coastguard Worker throw new Exception("Unable to find line " + line + " in " + m); 197*795d594fSAndroid Build Coastguard Worker } catch (Exception e) { 198*795d594fSAndroid Build Coastguard Worker throw new Exception("Unable to get line number info for " + m, e); 199*795d594fSAndroid Build Coastguard Worker } 200*795d594fSAndroid Build Coastguard Worker } 201*795d594fSAndroid Build Coastguard Worker } 202*795d594fSAndroid Build Coastguard Worker 203