Skip to content

Commit 27e3c45

Browse files
Support Data Breakpoint (#307)
* Support Data Breakpoint Signed-off-by: Jinbo Wang <[email protected]> * Address review comments Signed-off-by: Jinbo Wang <[email protected]> * Throw unsupported exception when set log message to data breakpoint Signed-off-by: Jinbo Wang <[email protected]>
1 parent fc45056 commit 27e3c45

17 files changed

+861
-46
lines changed

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSession.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ public IBreakpoint createBreakpoint(String className, int lineNumber, int hitCou
8181
return new EvaluatableBreakpoint(vm, this.getEventHub(), className, lineNumber, hitCount, condition, logMessage);
8282
}
8383

84+
@Override
85+
public IWatchpoint createWatchPoint(String className, String fieldName, String accessType, String condition, int hitCount) {
86+
return new Watchpoint(vm, this.getEventHub(), className, fieldName, accessType, condition, hitCount);
87+
}
88+
8489
@Override
8590
public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught) {
8691
EventRequestManager manager = vm.eventRequestManager();

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IDebugSession.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public interface IDebugSession {
3030
// breakpoints
3131
IBreakpoint createBreakpoint(String className, int lineNumber, int hitCount, String condition, String logMessage);
3232

33+
IWatchpoint createWatchPoint(String className, String fieldName, String accessType, String condition, int hitCount);
34+
3335
void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught);
3436

3537
// TODO: createFunctionBreakpoint

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/IEvaluatableBreakpoint.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,21 @@
1111

1212
package com.microsoft.java.debug.core;
1313

14-
public interface IEvaluatableBreakpoint extends IBreakpoint {
14+
public interface IEvaluatableBreakpoint {
1515
boolean containsEvaluatableExpression();
1616

1717
boolean containsConditionalExpression();
1818

1919
boolean containsLogpointExpression();
2020

21+
String getCondition();
22+
23+
void setCondition(String condition);
24+
25+
String getLogMessage();
26+
27+
void setLogMessage(String logMessage);
28+
2129
void setCompiledConditionalExpression(Object compiledExpression);
2230

2331
Object getCompiledConditionalExpression();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2019 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.core;
13+
14+
import java.util.concurrent.CompletableFuture;
15+
16+
public interface IWatchpoint extends IDebugResource {
17+
String className();
18+
19+
String fieldName();
20+
21+
String accessType();
22+
23+
CompletableFuture<IWatchpoint> install();
24+
25+
void putProperty(Object key, Object value);
26+
27+
Object getProperty(Object key);
28+
29+
int getHitCount();
30+
31+
void setHitCount(int hitCount);
32+
33+
String getCondition();
34+
35+
void setCondition(String condition);
36+
}
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2019 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.core;
13+
14+
import java.util.ArrayList;
15+
import java.util.HashMap;
16+
import java.util.List;
17+
import java.util.Objects;
18+
import java.util.concurrent.CompletableFuture;
19+
20+
import org.apache.commons.lang3.StringUtils;
21+
22+
import com.sun.jdi.Field;
23+
import com.sun.jdi.ReferenceType;
24+
import com.sun.jdi.VMDisconnectedException;
25+
import com.sun.jdi.VirtualMachine;
26+
import com.sun.jdi.event.ClassPrepareEvent;
27+
import com.sun.jdi.request.ClassPrepareRequest;
28+
import com.sun.jdi.request.EventRequest;
29+
import com.sun.jdi.request.WatchpointRequest;
30+
31+
import io.reactivex.Observable;
32+
import io.reactivex.disposables.Disposable;
33+
34+
public class Watchpoint implements IWatchpoint, IEvaluatableBreakpoint {
35+
private final VirtualMachine vm;
36+
private final IEventHub eventHub;
37+
private final String className;
38+
private final String fieldName;
39+
private String accessType = null;
40+
private String condition = null;
41+
private int hitCount;
42+
private HashMap<Object, Object> propertyMap = new HashMap<>();
43+
private Object compiledConditionalExpression = null;
44+
45+
// IDebugResource
46+
private List<EventRequest> requests = new ArrayList<>();
47+
private List<Disposable> subscriptions = new ArrayList<>();
48+
49+
Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName) {
50+
this(vm, eventHub, className, fieldName, "write");
51+
}
52+
53+
Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType) {
54+
this(vm, eventHub, className, fieldName, accessType, null, 0);
55+
}
56+
57+
Watchpoint(VirtualMachine vm, IEventHub eventHub, String className, String fieldName, String accessType, String condition, int hitCount) {
58+
Objects.requireNonNull(vm);
59+
Objects.requireNonNull(eventHub);
60+
Objects.requireNonNull(className);
61+
Objects.requireNonNull(fieldName);
62+
this.vm = vm;
63+
this.eventHub = eventHub;
64+
this.className = className;
65+
this.fieldName = fieldName;
66+
this.accessType = accessType;
67+
this.condition = condition;
68+
this.hitCount = hitCount;
69+
}
70+
71+
@Override
72+
public List<EventRequest> requests() {
73+
return requests;
74+
}
75+
76+
@Override
77+
public List<Disposable> subscriptions() {
78+
return subscriptions;
79+
}
80+
81+
@Override
82+
public void close() throws Exception {
83+
try {
84+
vm.eventRequestManager().deleteEventRequests(requests());
85+
} catch (VMDisconnectedException ex) {
86+
// ignore since removing breakpoints is meaningless when JVM is terminated.
87+
}
88+
subscriptions().forEach(subscription -> {
89+
subscription.dispose();
90+
});
91+
requests.clear();
92+
subscriptions.clear();
93+
}
94+
95+
@Override
96+
public String className() {
97+
return className;
98+
}
99+
100+
@Override
101+
public String fieldName() {
102+
return fieldName;
103+
}
104+
105+
@Override
106+
public String accessType() {
107+
return accessType;
108+
}
109+
110+
@Override
111+
public String getCondition() {
112+
return condition;
113+
}
114+
115+
@Override
116+
public void setCondition(String condition) {
117+
this.condition = condition;
118+
setCompiledConditionalExpression(null);
119+
}
120+
121+
@Override
122+
public int getHitCount() {
123+
return hitCount;
124+
}
125+
126+
@Override
127+
public void setHitCount(int hitCount) {
128+
this.hitCount = hitCount;
129+
130+
Observable.fromIterable(this.requests())
131+
.filter(request -> request instanceof WatchpointRequest)
132+
.subscribe(request -> {
133+
request.addCountFilter(hitCount);
134+
request.enable();
135+
});
136+
}
137+
138+
@Override
139+
public void putProperty(Object key, Object value) {
140+
propertyMap.put(key, value);
141+
}
142+
143+
@Override
144+
public Object getProperty(Object key) {
145+
return propertyMap.get(key);
146+
}
147+
148+
@Override
149+
public CompletableFuture<IWatchpoint> install() {
150+
// It's possible that different class loaders create new class with the same name.
151+
// Here to listen to future class prepare events to handle such case.
152+
ClassPrepareRequest classPrepareRequest = vm.eventRequestManager().createClassPrepareRequest();
153+
classPrepareRequest.addClassFilter(className);
154+
classPrepareRequest.enable();
155+
requests.add(classPrepareRequest);
156+
157+
CompletableFuture<IWatchpoint> future = new CompletableFuture<>();
158+
Disposable subscription = eventHub.events()
159+
.filter(debugEvent -> debugEvent.event instanceof ClassPrepareEvent && (classPrepareRequest.equals(debugEvent.event.request())))
160+
.subscribe(debugEvent -> {
161+
ClassPrepareEvent event = (ClassPrepareEvent) debugEvent.event;
162+
List<WatchpointRequest> watchpointRequests = createWatchpointRequests(event.referenceType());
163+
requests.addAll(watchpointRequests);
164+
if (!watchpointRequests.isEmpty() && !future.isDone()) {
165+
this.putProperty("verified", true);
166+
future.complete(this);
167+
}
168+
});
169+
subscriptions.add(subscription);
170+
171+
List<EventRequest> watchpointRequests = new ArrayList<>();
172+
List<ReferenceType> types = vm.classesByName(className);
173+
for (ReferenceType type : types) {
174+
watchpointRequests.addAll(createWatchpointRequests(type));
175+
}
176+
177+
requests.addAll(watchpointRequests);
178+
if (!watchpointRequests.isEmpty() && !future.isDone()) {
179+
this.putProperty("verified", true);
180+
future.complete(this);
181+
}
182+
183+
return future;
184+
}
185+
186+
private List<WatchpointRequest> createWatchpointRequests(ReferenceType type) {
187+
List<WatchpointRequest> watchpointRequests = new ArrayList<>();
188+
Field field = type.fieldByName(fieldName);
189+
if (field != null) {
190+
if ("read".equals(accessType)) {
191+
watchpointRequests.add(vm.eventRequestManager().createAccessWatchpointRequest(field));
192+
} else if ("readWrite".equals(accessType)) {
193+
watchpointRequests.add(vm.eventRequestManager().createAccessWatchpointRequest(field));
194+
watchpointRequests.add(vm.eventRequestManager().createModificationWatchpointRequest(field));
195+
} else {
196+
watchpointRequests.add(vm.eventRequestManager().createModificationWatchpointRequest(field));
197+
}
198+
}
199+
200+
watchpointRequests.forEach(request -> {
201+
request.setSuspendPolicy(WatchpointRequest.SUSPEND_EVENT_THREAD);
202+
if (hitCount > 0) {
203+
request.addCountFilter(hitCount);
204+
}
205+
request.enable();
206+
});
207+
return watchpointRequests;
208+
}
209+
210+
@Override
211+
public String getLogMessage() {
212+
return null;
213+
}
214+
215+
@Override
216+
public void setLogMessage(String logMessage) {
217+
throw new UnsupportedOperationException("Log message feature is unsupported for watchpoint.");
218+
}
219+
220+
@Override
221+
public boolean containsEvaluatableExpression() {
222+
return containsConditionalExpression();
223+
}
224+
225+
@Override
226+
public boolean containsConditionalExpression() {
227+
return StringUtils.isNotBlank(getCondition());
228+
}
229+
230+
@Override
231+
public boolean containsLogpointExpression() {
232+
return false;
233+
}
234+
235+
public void setCompiledConditionalExpression(Object compiledExpression) {
236+
this.compiledConditionalExpression = compiledExpression;
237+
}
238+
239+
public Object getCompiledConditionalExpression() {
240+
return compiledConditionalExpression;
241+
}
242+
243+
@Override
244+
public void setCompiledLogpointExpression(Object compiledExpression) {
245+
// do nothing
246+
}
247+
248+
@Override
249+
public Object getCompiledLogpointExpression() {
250+
return null;
251+
}
252+
}

0 commit comments

Comments
 (0)