blob: bbb89f707fb26f10beeb79c3b57542a26923d45e [file] [log] [blame]
Alex Light9fb1ab12017-09-05 09:32:49 -07001/*
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
17package art;
18
19import java.lang.reflect.Executable;
20import java.util.HashSet;
21import java.util.Set;
22import java.util.Objects;
23
24public 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