2424
2525import java .lang .reflect .Modifier ;
2626import java .util .ArrayList ;
27- import java .util .HashMap ;
27+ import java .util .Collection ;
2828import java .util .List ;
2929import java .util .Map ;
3030import 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
3335import com .oracle .truffle .api .CompilerDirectives .TruffleBoundary ;
3436import com .oracle .truffle .api .debug .Breakpoint ;
4042import com .oracle .truffle .espresso .jdwp .impl .BreakpointInfo ;
4143import com .oracle .truffle .espresso .jdwp .impl .ClassPrepareRequest ;
4244import com .oracle .truffle .espresso .jdwp .impl .DebuggerController ;
45+ import com .oracle .truffle .espresso .jdwp .impl .EventInfo ;
4346import com .oracle .truffle .espresso .jdwp .impl .FieldBreakpointEvent ;
4447import com .oracle .truffle .espresso .jdwp .impl .FieldBreakpointInfo ;
4548import 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
0 commit comments