Skip to content

Commit fb11cc9

Browse files
committed
Event filters reworked, method breakpoints submitted to classes as they are loaded. (GR-68719)
1 parent 737c7b6 commit fb11cc9

File tree

12 files changed

+436
-231
lines changed

12 files changed

+436
-231
lines changed

espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/VMEventListener.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -22,6 +22,8 @@
2222
*/
2323
package com.oracle.truffle.espresso.jdwp.api;
2424

25+
import java.util.function.Consumer;
26+
2527
import com.oracle.truffle.espresso.jdwp.impl.BreakpointInfo;
2628
import com.oracle.truffle.espresso.jdwp.impl.ClassPrepareRequest;
2729
import com.oracle.truffle.espresso.jdwp.impl.FieldBreakpointEvent;
@@ -40,6 +42,10 @@ public interface VMEventListener extends VMListener {
4042

4143
boolean vmDied();
4244

45+
void addClassConsumer(Consumer<KlassRef> classConsumer);
46+
47+
void removeClassConsumer(Consumer<KlassRef> classConsumer);
48+
4349
void addClassUnloadRequestId(int id);
4450

4551
void addThreadStartedRequestId(int id, byte suspendPolicy);

espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/VMEventListenerImpl.java

Lines changed: 66 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@
2424

2525
import java.lang.reflect.Modifier;
2626
import java.util.ArrayList;
27-
import java.util.HashMap;
27+
import java.util.Collection;
2828
import java.util.List;
2929
import java.util.Map;
3030
import java.util.concurrent.Callable;
31-
import java.util.regex.Pattern;
31+
import java.util.concurrent.ConcurrentHashMap;
32+
import java.util.concurrent.CopyOnWriteArrayList;
33+
import java.util.function.Consumer;
3234

3335
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
3436
import com.oracle.truffle.api.debug.Breakpoint;
@@ -40,6 +42,7 @@
4042
import com.oracle.truffle.espresso.jdwp.impl.BreakpointInfo;
4143
import com.oracle.truffle.espresso.jdwp.impl.ClassPrepareRequest;
4244
import com.oracle.truffle.espresso.jdwp.impl.DebuggerController;
45+
import com.oracle.truffle.espresso.jdwp.impl.EventInfo;
4346
import com.oracle.truffle.espresso.jdwp.impl.FieldBreakpointEvent;
4447
import com.oracle.truffle.espresso.jdwp.impl.FieldBreakpointInfo;
4548
import com.oracle.truffle.espresso.jdwp.impl.JDWP;
@@ -58,13 +61,14 @@ public final class VMEventListenerImpl implements VMEventListener {
5861

5962
public static final InteropLibrary UNCACHED = InteropLibrary.getUncached();
6063

61-
private final HashMap<Integer, ClassPrepareRequest> classPrepareRequests = new HashMap<>();
62-
private final HashMap<Integer, BreakpointInfo> breakpointRequests = new HashMap<>();
63-
private final HashMap<Integer, RequestFilter> monitorContendedRequests = new HashMap<>();
64-
private final HashMap<Integer, RequestFilter> monitorContendedEnteredRequests = new HashMap<>();
65-
private final HashMap<Integer, RequestFilter> monitorWaitRequests = new HashMap<>();
66-
private final HashMap<Integer, RequestFilter> monitorWaitedRequests = new HashMap<>();
67-
private final HashMap<Integer, FieldRef> fieldRequests = new HashMap<>();
64+
private final Map<Integer, ClassPrepareRequest> classPrepareRequests = new ConcurrentHashMap<>();
65+
private final Map<Integer, BreakpointInfo> breakpointRequests = new ConcurrentHashMap<>();
66+
private final Map<Integer, RequestFilter> monitorContendedRequests = new ConcurrentHashMap<>();
67+
private final Map<Integer, RequestFilter> monitorContendedEnteredRequests = new ConcurrentHashMap<>();
68+
private final Map<Integer, RequestFilter> monitorWaitRequests = new ConcurrentHashMap<>();
69+
private final Map<Integer, RequestFilter> monitorWaitedRequests = new ConcurrentHashMap<>();
70+
private final Map<Integer, FieldRef> fieldRequests = new ConcurrentHashMap<>();
71+
private final Collection<Consumer<KlassRef>> classConsumers = new CopyOnWriteArrayList<>();
6872

6973
// The connection field is null only until the connection is established. Thus, we need
7074
// to guard any attempted usage prior to that, e.g. vm dies event.
@@ -82,7 +86,7 @@ public final class VMEventListenerImpl implements VMEventListener {
8286
private byte vmDeathSuspendPolicy = SuspendStrategy.NONE;
8387
private int vmStartRequestId;
8488
private final List<PacketStream> heldEvents = new ArrayList<>();
85-
private final Map<Object, Object> currentContendedMonitor = new HashMap<>();
89+
private final Map<Object, Object> currentContendedMonitor = new ConcurrentHashMap<>();
8690
private Object initialThread;
8791

8892
public void activate(Object mainThread, DebuggerController control, JDWPContext jdwpContext) {
@@ -125,19 +129,25 @@ public void setConnection(SocketConnection connection) {
125129
this.connection = connection;
126130
}
127131

132+
@Override
133+
public void addClassConsumer(Consumer<KlassRef> classConsumer) {
134+
classConsumers.add(classConsumer);
135+
}
136+
137+
@Override
138+
public void removeClassConsumer(Consumer<KlassRef> classConsumer) {
139+
classConsumers.remove(classConsumer);
140+
}
141+
128142
@Override
129143
public void addClassPrepareRequest(ClassPrepareRequest request) {
130-
synchronized (classPrepareRequests) {
131-
classPrepareRequests.put(request.getRequestId(), request);
132-
}
144+
classPrepareRequests.put(request.getRequestId(), request);
133145
}
134146

135147
@Override
136148
@TruffleBoundary
137149
public void removeClassPrepareRequest(int requestId) {
138-
synchronized (classPrepareRequests) {
139-
classPrepareRequests.remove(requestId);
140-
}
150+
classPrepareRequests.remove(requestId);
141151
}
142152

143153
@Override
@@ -159,6 +169,7 @@ public void removeBreakpointRequest(int requestId) {
159169

160170
@Override
161171
public void clearAllBreakpointRequests() {
172+
classConsumers.clear();
162173
breakpointRequests.clear();
163174
}
164175

@@ -286,25 +297,22 @@ public boolean onMethodReturn(MethodRef method, Node node, Object returnValue) {
286297
@Override
287298
@TruffleBoundary
288299
public void classPrepared(KlassRef klass, Object prepareThread) {
300+
for (Consumer<KlassRef> c : classConsumers) {
301+
c.accept(klass);
302+
}
289303
// check if event should be reported based on the current patterns, otherwise return early
290304
if (classPrepareRequests.isEmpty()) {
291305
return;
292306
}
293307

294-
String dotName = klass.getNameAsString().replace('/', '.');
295308
ArrayList<ClassPrepareRequest> toSend = new ArrayList<>();
296309
byte suspendPolicy = SuspendStrategy.NONE;
297310

298-
// take a snapshot of the current class prepare events
299-
// to avoid concurrent modification exceptions
300-
ClassPrepareRequest[] prepareRequests;
301-
synchronized (classPrepareRequests) {
302-
prepareRequests = classPrepareRequests.values().toArray(new ClassPrepareRequest[0]);
303-
}
304-
for (ClassPrepareRequest cpr : prepareRequests) {
305-
Pattern[] patterns = cpr.getPatterns();
306-
for (Pattern pattern : patterns) {
307-
if ("".equals(pattern.pattern()) || pattern.matcher(dotName).matches()) {
311+
Collection<ClassPrepareRequest> prepareRequests = classPrepareRequests.values();
312+
if (!prepareRequests.isEmpty()) {
313+
EventInfo event = new EventInfo.Klass(klass, prepareThread);
314+
for (ClassPrepareRequest cpr : prepareRequests) {
315+
if (cpr.isHit(event)) {
308316
toSend.add(cpr);
309317
byte cprPolicy = cpr.getSuspendPolicy();
310318
if (cprPolicy == SuspendStrategy.ALL) {
@@ -313,6 +321,9 @@ public void classPrepared(KlassRef klass, Object prepareThread) {
313321
suspendPolicy = SuspendStrategy.EVENT_THREAD;
314322
}
315323
}
324+
if (!cpr.isActive()) {
325+
classPrepareRequests.remove(cpr.getRequestId());
326+
}
316327
}
317328
}
318329
if (!toSend.isEmpty()) {
@@ -700,18 +711,21 @@ public void monitorWait(Object monitor, long timeout) {
700711
if (monitorWaitRequests.isEmpty()) {
701712
return;
702713
}
714+
CallFrame frame = context.locateObjectWaitFrame();
715+
EventInfo event = new EventInfo.Frame(context, frame, guestThread);
703716
for (Map.Entry<Integer, RequestFilter> entry : monitorWaitRequests.entrySet()) {
704717
RequestFilter filter = entry.getValue();
705-
if (guestThread == filter.getThread()) {
718+
if (filter.isHit(event)) {
706719
// monitor wait(timeout) is called on a requested thread
707720
// create the call frame for the caller location of Object.wait(timeout)
708-
CallFrame frame = context.locateObjectWaitFrame();
709-
710721
debuggerController.immediateSuspend(guestThread, filter.getSuspendPolicy(), () -> {
711722
sendMonitorWaitEvent(monitor, timeout, filter, frame);
712723
return null;
713724
});
714725
}
726+
if (!filter.isActive()) {
727+
monitorWaitRequests.remove(filter.getRequestId());
728+
}
715729
}
716730
}
717731

@@ -770,12 +784,13 @@ public void monitorWaited(Object monitor, boolean timedOut) {
770784
if (monitorWaitedRequests.isEmpty()) {
771785
return;
772786
}
787+
CallFrame frame = context.locateObjectWaitFrame();
788+
EventInfo event = new EventInfo.Frame(context, frame, currentThread);
773789
for (Map.Entry<Integer, RequestFilter> entry : monitorWaitedRequests.entrySet()) {
774790
RequestFilter filter = entry.getValue();
775-
if (currentThread == filter.getThread()) {
791+
if (filter.isHit(event)) {
776792
// monitor wait(timeout) is called on a requested thread
777793
// create the call frame for the caller location of Object.wait(timeout)
778-
CallFrame frame = context.locateObjectWaitFrame();
779794

780795
debuggerController.immediateSuspend(currentThread, filter.getSuspendPolicy(), new Callable<Void>() {
781796
@Override
@@ -785,6 +800,9 @@ public Void call() {
785800
}
786801
});
787802
}
803+
if (!filter.isActive()) {
804+
monitorWaitedRequests.remove(filter.getRequestId());
805+
}
788806
}
789807
}
790808

@@ -801,18 +819,22 @@ public void onContendedMonitorEnter(Object monitor) {
801819
return;
802820
}
803821

822+
final CallFrame topFrame = context.getStackTrace(guestThread)[0];
823+
EventInfo event = new EventInfo.Frame(context, topFrame, guestThread);
804824
for (Map.Entry<Integer, RequestFilter> entry : monitorContendedRequests.entrySet()) {
805825
RequestFilter filter = entry.getValue();
806-
if (guestThread == filter.getThread()) {
826+
if (filter.isHit(event)) {
807827
// monitor is contended on a requested thread
808-
MonitorEvent event = new MonitorEvent(monitor, filter);
809-
final CallFrame topFrame = context.getStackTrace(guestThread)[0];
828+
MonitorEvent mevent = new MonitorEvent(monitor, filter);
810829

811830
debuggerController.immediateSuspend(guestThread, filter.getSuspendPolicy(), () -> {
812-
sendMonitorContendedEnterEvent(event, topFrame);
831+
sendMonitorContendedEnterEvent(mevent, topFrame);
813832
return null;
814833
});
815834
}
835+
if (!filter.isActive()) {
836+
monitorContendedRequests.remove(filter.getRequestId());
837+
}
816838
}
817839
}
818840

@@ -829,18 +851,22 @@ public void onContendedMonitorEntered(Object monitor) {
829851
return;
830852
}
831853

854+
final CallFrame topFrame = context.getStackTrace(guestThread)[0];
855+
EventInfo event = new EventInfo.Frame(context, topFrame, guestThread);
832856
for (Map.Entry<Integer, RequestFilter> entry : monitorContendedEnteredRequests.entrySet()) {
833857
RequestFilter filter = entry.getValue();
834-
if (guestThread == filter.getThread()) {
858+
if (filter.isHit(event)) {
835859
// monitor is contended on a requested thread
836-
MonitorEvent event = new MonitorEvent(monitor, filter);
837-
final CallFrame topFrame = context.getStackTrace(guestThread)[0];
860+
MonitorEvent mevent = new MonitorEvent(monitor, filter);
838861

839862
debuggerController.immediateSuspend(guestThread, filter.getSuspendPolicy(), () -> {
840-
sendMonitorContendedEnteredEvent(event, topFrame);
863+
sendMonitorContendedEnteredEvent(mevent, topFrame);
841864
return null;
842865
});
843866
}
867+
if (!filter.isActive()) {
868+
monitorContendedEnteredRequests.remove(filter.getRequestId());
869+
}
844870
}
845871
}
846872

espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/AbstractBreakpointInfo.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,6 @@ public boolean isUnCaught() {
8888
return false;
8989
}
9090

91-
@Override
92-
public Object getThread() {
93-
return null;
94-
}
95-
9691
@Override
9792
public long getClassId() {
9893
return 0;

espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/BreakpointInfo.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ public interface BreakpointInfo {
4343

4444
boolean isUnCaught();
4545

46-
Object getThread();
47-
4846
long getClassId();
4947

5048
long getMethodId();

espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/ClassPrepareRequest.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -22,8 +22,6 @@
2222
*/
2323
package com.oracle.truffle.espresso.jdwp.impl;
2424

25-
import java.util.regex.Pattern;
26-
2725
public final class ClassPrepareRequest {
2826

2927
private final RequestFilter filter;
@@ -32,19 +30,16 @@ public final class ClassPrepareRequest {
3230
this.filter = filter;
3331
}
3432

35-
public Pattern[] getPatterns() {
36-
if (filter.getIncludePatterns().length == 0) {
37-
return new Pattern[]{Pattern.compile("")};
38-
}
39-
return filter.getIncludePatterns();
33+
public boolean isHit(EventInfo event) {
34+
return filter.isHit(event);
4035
}
4136

42-
public int getRequestId() {
43-
return filter.getRequestId();
37+
public boolean isActive() {
38+
return filter.isActive();
4439
}
4540

46-
public Object getThread() {
47-
return filter.getThread();
41+
public int getRequestId() {
42+
return filter.getRequestId();
4843
}
4944

5045
public byte getSuspendPolicy() {

0 commit comments

Comments
 (0)