203 lines
5.9 KiB
Java
203 lines
5.9 KiB
Java
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package art;
|
|
|
|
import java.lang.reflect.Executable;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
import java.util.Objects;
|
|
|
|
public class Breakpoint {
|
|
public static class Manager {
|
|
public static class BP {
|
|
public final Executable method;
|
|
public final long location;
|
|
|
|
public BP(Executable method) {
|
|
this(method, getStartLocation(method));
|
|
}
|
|
|
|
public BP(Executable method, long location) {
|
|
this.method = method;
|
|
this.location = location;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object other) {
|
|
return (other instanceof BP) &&
|
|
method.equals(((BP)other).method) &&
|
|
location == ((BP)other).location;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return method.toString() + " @ " + getLine();
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(method, location);
|
|
}
|
|
|
|
public int getLine() {
|
|
try {
|
|
LineNumber[] lines = getLineNumberTable(method);
|
|
int best = -1;
|
|
for (LineNumber l : lines) {
|
|
if (l.location > location) {
|
|
break;
|
|
} else {
|
|
best = l.line;
|
|
}
|
|
}
|
|
return best;
|
|
} catch (Exception e) {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
private Set<BP> breaks = new HashSet<>();
|
|
|
|
public void setBreakpoints(BP... bs) {
|
|
for (BP b : bs) {
|
|
if (breaks.add(b)) {
|
|
Breakpoint.setBreakpoint(b.method, b.location);
|
|
}
|
|
}
|
|
}
|
|
public void setBreakpoint(Executable method, long location) {
|
|
setBreakpoints(new BP(method, location));
|
|
}
|
|
|
|
public void clearBreakpoints(BP... bs) {
|
|
for (BP b : bs) {
|
|
if (breaks.remove(b)) {
|
|
Breakpoint.clearBreakpoint(b.method, b.location);
|
|
}
|
|
}
|
|
}
|
|
public void clearBreakpoint(Executable method, long location) {
|
|
clearBreakpoints(new BP(method, location));
|
|
}
|
|
|
|
public void clearAllBreakpoints() {
|
|
clearBreakpoints(breaks.toArray(new BP[0]));
|
|
}
|
|
}
|
|
|
|
public static void startBreakpointWatch(Class<?> methodClass,
|
|
Executable breakpointReached,
|
|
Thread thr) {
|
|
startBreakpointWatch(methodClass, breakpointReached, false, thr);
|
|
}
|
|
|
|
/**
|
|
* Enables the trapping of breakpoint events.
|
|
*
|
|
* If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
|
|
*/
|
|
public static native void startBreakpointWatch(Class<?> methodClass,
|
|
Executable breakpointReached,
|
|
boolean allowRecursive,
|
|
Thread thr);
|
|
public static native void stopBreakpointWatch(Thread thr);
|
|
|
|
public static final class LineNumber implements Comparable<LineNumber> {
|
|
public final long location;
|
|
public final int line;
|
|
|
|
private LineNumber(long loc, int line) {
|
|
this.location = loc;
|
|
this.line = line;
|
|
}
|
|
|
|
public boolean equals(Object other) {
|
|
return other instanceof LineNumber && ((LineNumber)other).line == line &&
|
|
((LineNumber)other).location == location;
|
|
}
|
|
|
|
public int compareTo(LineNumber other) {
|
|
int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
|
|
if (v != 0) {
|
|
return v;
|
|
} else {
|
|
return Long.valueOf(location).compareTo(Long.valueOf(other.location));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static native void setBreakpoint(Executable m, long loc);
|
|
public static void setBreakpoint(Executable m, LineNumber l) {
|
|
setBreakpoint(m, l.location);
|
|
}
|
|
|
|
public static native void clearBreakpoint(Executable m, long loc);
|
|
public static void clearBreakpoint(Executable m, LineNumber l) {
|
|
clearBreakpoint(m, l.location);
|
|
}
|
|
|
|
private static native Object[] getLineNumberTableNative(Executable m);
|
|
public static LineNumber[] getLineNumberTable(Executable m) {
|
|
Object[] nativeTable = getLineNumberTableNative(m);
|
|
long[] location = (long[])(nativeTable[0]);
|
|
int[] lines = (int[])(nativeTable[1]);
|
|
if (lines.length != location.length) {
|
|
throw new Error("Lines and locations have different lengths!");
|
|
}
|
|
LineNumber[] out = new LineNumber[lines.length];
|
|
for (int i = 0; i < lines.length; i++) {
|
|
out[i] = new LineNumber(location[i], lines[i]);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
public static native long getStartLocation(Executable m);
|
|
|
|
public static int locationToLine(Executable m, long location) {
|
|
try {
|
|
Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
|
|
int best = -1;
|
|
for (Breakpoint.LineNumber l : lines) {
|
|
if (l.location > location) {
|
|
break;
|
|
} else {
|
|
best = l.line;
|
|
}
|
|
}
|
|
return best;
|
|
} catch (Exception e) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
public static long lineToLocation(Executable m, int line) throws Exception {
|
|
try {
|
|
Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
|
|
for (Breakpoint.LineNumber l : lines) {
|
|
if (l.line == line) {
|
|
return l.location;
|
|
}
|
|
}
|
|
throw new Exception("Unable to find line " + line + " in " + m);
|
|
} catch (Exception e) {
|
|
throw new Exception("Unable to get line number info for " + m, e);
|
|
}
|
|
}
|
|
}
|
|
|