1 19 20 package org.netbeans.modules.debugger.jpda; 21 22 import com.sun.jdi.AbsentInformationException; 23 import com.sun.jdi.IncompatibleThreadStateException; 24 import com.sun.jdi.event.LocatableEvent; 25 import java.beans.PropertyChangeListener ; 26 import java.lang.reflect.InvocationTargetException ; 27 import java.net.MalformedURLException ; 28 import java.net.URL ; 29 import java.awt.Dialog ; 30 import java.awt.event.ActionEvent ; 31 import java.awt.event.ActionListener ; 32 import java.util.ArrayList ; 33 import java.util.HashMap ; 34 import java.util.HashSet ; 35 import java.util.Iterator ; 36 import java.util.List ; 37 import java.util.Map ; 38 import java.util.Set ; 39 import java.util.logging.Level ; 40 import java.util.logging.Logger ; 41 import org.netbeans.api.debugger.DebuggerManager; 42 import org.netbeans.api.debugger.Session; 43 import org.netbeans.api.debugger.jpda.CallStackFrame; 44 import org.netbeans.api.debugger.jpda.JPDABreakpoint; 45 import org.netbeans.api.debugger.jpda.JPDAStep; 46 import org.netbeans.api.debugger.jpda.JPDAThread; 47 import org.netbeans.api.debugger.jpda.JPDADebugger; 48 import com.sun.jdi.event.Event; 49 import com.sun.jdi.request.BreakpointRequest; 50 import com.sun.jdi.ThreadReference; 51 import com.sun.jdi.request.StepRequest; 52 import com.sun.jdi.request.EventRequest; 53 import com.sun.jdi.request.EventRequestManager; 54 import com.sun.jdi.Location; 55 import com.sun.jdi.Method; 56 import com.sun.jdi.ReferenceType; 57 import com.sun.jdi.StackFrame; 58 import com.sun.jdi.ThreadReference; 59 import com.sun.jdi.VirtualMachine; 60 import org.netbeans.api.debugger.jpda.JPDAThreadGroup; 61 import org.netbeans.api.debugger.jpda.MethodBreakpoint; 62 import org.netbeans.api.debugger.jpda.SmartSteppingFilter; 63 import org.netbeans.api.debugger.jpda.Variable; 64 import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent; 65 import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener; 66 import org.netbeans.modules.debugger.jpda.JPDAStepImpl.SingleThreadedStepWatch; 67 import org.netbeans.modules.debugger.jpda.actions.SmartSteppingFilterImpl; 68 import org.netbeans.modules.debugger.jpda.breakpoints.MethodBreakpointImpl; 69 import org.netbeans.modules.debugger.jpda.util.Executor; 70 import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl; 71 import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl; 72 import org.netbeans.spi.debugger.jpda.EditorContext; 73 import org.netbeans.spi.debugger.jpda.EditorContext.Operation; 74 import org.openide.DialogDescriptor; 75 import org.openide.DialogDescriptor; 76 import org.openide.ErrorManager; 77 import org.openide.NotifyDescriptor; 78 import org.openide.filesystems.FileObject; 79 import org.openide.filesystems.URLMapper; 80 import org.openide.util.NbBundle; 81 import org.openide.util.RequestProcessor; 82 83 84 public class JPDAStepImpl extends JPDAStep implements Executor { 85 86 private static Logger logger = Logger.getLogger("org.netbeans.modules.debugger.jpda.step"); 88 private static final boolean IS_JDK_16 = !System.getProperty("java.version").startsWith("1.5"); 90 91 private Operation[] currentOperations; 93 private Operation lastOperation; 94 private MethodExitBreakpointListener lastMethodExitBreakpointListener; 95 private Set <BreakpointRequest> operationBreakpoints; 96 private StepRequest boundaryStepRequest; 97 private SingleThreadedStepWatch stepWatch; 98 99 private Session session; 100 101 public JPDAStepImpl(JPDADebugger debugger, Session session, int size, int depth) { 102 super(debugger, size, depth); 103 this.session = session; 104 } 105 106 public void addStep(JPDAThread tr) { 107 JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger; 108 JPDAThreadImpl trImpl = (JPDAThreadImpl) tr; 109 VirtualMachine vm = debuggerImpl.getVirtualMachine(); 110 if (vm == null) { 111 return ; } 113 EventRequestManager erm = vm.eventRequestManager(); 114 List <StepRequest> stepRequests = erm.stepRequests(); 116 erm.deleteEventRequests(stepRequests); 117 for (StepRequest stepRequest : stepRequests) { 118 SingleThreadedStepWatch.stepRequestDeleted(stepRequest); 119 } 120 int size = getSize(); 121 boolean stepAdded = false; 122 logger.log(Level.FINE, "Step "+((size == JPDAStep.STEP_OPERATION) ? "operation" : "line") 123 +" "+((getDepth() == JPDAStep.STEP_INTO) ? "into" : 124 ((getDepth() == JPDAStep.STEP_OVER) ? "over" : "out")) 125 +" in thread "+tr.getName()); 126 if (size == JPDAStep.STEP_OPERATION) { 127 stepAdded = addOperationStep(trImpl, false); 128 if (!stepAdded) { 129 size = JPDAStep.STEP_LINE; 130 logger.log(Level.FINE, "Operation step changed to line step"); 131 } 132 } 133 if (!stepAdded) { 134 StepRequest stepRequest = vm.eventRequestManager().createStepRequest( 135 trImpl.getThreadReference(), 136 size, 137 getDepth() 138 ); 139 stepRequest.addCountFilter(1); 140 debuggerImpl.getOperator().register(stepRequest, this); 141 stepRequest.setSuspendPolicy(debugger.getSuspend()); 142 143 try { 144 stepRequest.enable (); 145 } catch (IllegalThreadStateException itsex) { 146 debuggerImpl.getOperator().unregister(stepRequest); 148 stepRequest = null; 149 } 150 151 if (stepRequest != null && stepRequest.suspendPolicy() == StepRequest.SUSPEND_EVENT_THREAD) { 152 stepWatch = new SingleThreadedStepWatch(debuggerImpl, stepRequest); 153 } 154 } 155 } 156 157 private boolean addOperationStep(JPDAThreadImpl tr, boolean lineStepExec) { 158 ThreadReference trRef = tr.getThreadReference(); 159 StackFrame sf; 160 try { 161 sf = trRef.frame(0); 162 } catch (IncompatibleThreadStateException itsex) { 163 return false; 164 } 165 Location loc = sf.location(); 166 Session currentSession = DebuggerManager.getDebuggerManager().getCurrentSession(); 167 String language = currentSession == null ? null : currentSession.getCurrentLanguage(); 168 SourcePath sourcePath = ((JPDADebuggerImpl) debugger).getEngineContext(); 169 String url = sourcePath.getURL(loc, language); 170 ExpressionPool exprPool = ((JPDADebuggerImpl) debugger).getExpressionPool(); 171 ExpressionPool.Expression expr = exprPool.getExpressionAt(loc, url); 172 if (expr == null) { 173 return false; 174 } 175 Operation[] ops = expr.getOperations(); 176 177 int opIndex = -1; 179 int codeIndex = (int) loc.codeIndex(); 180 if (codeIndex <= ops[0].getBytecodeIndex()) { 181 if (!lineStepExec) { 182 tr.clearLastOperations(); 183 } 184 if (!ops[0].equals(tr.getCurrentOperation())) { 186 opIndex = expr.findNextOperationIndex(codeIndex - 1); 187 if (opIndex >= 0 && ops[opIndex].getBytecodeIndex() == codeIndex) { 188 tr.setCurrentOperation(ops[opIndex]); 189 if (lineStepExec) { 190 return false; 191 } 192 if (! getHidden()) { 193 JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl)debugger; 194 debuggerImpl.setStoppedStateNoContinue(tr.getThreadReference()); 195 } 196 return true; 197 } 198 } 199 } 200 this.lastOperation = tr.getCurrentOperation(); 201 VirtualMachine vm = loc.virtualMachine(); 202 if (lastOperation != null) { 203 String methodName = lastOperation.getMethodName(); 205 if (methodName != null && MethodBreakpointImpl.canGetMethodReturnValues(vm)) { 206 MethodBreakpoint mb = MethodBreakpoint.create(lastOperation.getMethodClassType(), methodName); 207 mb.setBreakpointType(MethodBreakpoint.TYPE_METHOD_EXIT); 209 mb.setHidden(true); 210 mb.setSuspend(JPDABreakpoint.SUSPEND_NONE); 211 lastMethodExitBreakpointListener = new MethodExitBreakpointListener(mb); 212 mb.addJPDABreakpointListener(lastMethodExitBreakpointListener); 213 DebuggerManager.getDebuggerManager().addBreakpoint(mb); 214 } 215 } 216 tr.keepLastOperationsUponResume(); 217 ExpressionPool.OperationLocation[] nextOperationLocations; 218 if (opIndex < 0) { 219 nextOperationLocations = expr.findNextOperationLocations(codeIndex); 220 } else { 221 Location[] locations = expr.getLocations(); 222 nextOperationLocations = new ExpressionPool.OperationLocation[] { 223 new ExpressionPool.OperationLocation(ops[opIndex], locations[opIndex], opIndex) }; 224 } 225 boolean isNextOperationFromDifferentExpression = false; 226 if (nextOperationLocations != null) { 227 239 this.operationBreakpoints = new HashSet <BreakpointRequest>(); 240 for (int ni = 0; ni < nextOperationLocations.length; ni++) { 243 Location nloc = nextOperationLocations[ni].getLocation(); 244 if (nextOperationLocations[ni].getIndex() < 0) { 245 isNextOperationFromDifferentExpression = true; 246 Operation[] newOps = new Operation[ops.length + 1]; 247 System.arraycopy(ops, 0, newOps, 0, ops.length); 248 newOps[ops.length] = nextOperationLocations[ni].getOperation(); 249 ops = newOps; 250 } 251 BreakpointRequest brReq = vm.eventRequestManager().createBreakpointRequest(nloc); 252 operationBreakpoints.add(brReq); 253 ((JPDADebuggerImpl) debugger).getOperator().register(brReq, this); 254 brReq.setSuspendPolicy(debugger.getSuspend()); 255 brReq.addThreadFilter(trRef); 256 brReq.enable(); 257 } 258 } else if (lineStepExec) { 259 return false; 260 } 261 262 boundaryStepRequest = vm.eventRequestManager().createStepRequest( 264 tr.getThreadReference(), 265 StepRequest.STEP_LINE, 266 StepRequest.STEP_OVER 267 ); 268 if (isNextOperationFromDifferentExpression) { 269 boundaryStepRequest.addCountFilter(2); 270 } else { 271 boundaryStepRequest.addCountFilter(1); 272 } 273 ((JPDADebuggerImpl) debugger).getOperator().register(boundaryStepRequest, this); 274 boundaryStepRequest.setSuspendPolicy(debugger.getSuspend()); 275 try { 276 boundaryStepRequest.enable (); 277 } catch (IllegalThreadStateException itsex) { 278 ((JPDADebuggerImpl) debugger).getOperator().unregister(boundaryStepRequest); 280 boundaryStepRequest = null; 281 return false; 282 } 283 284 this.currentOperations = ops; 285 return true; 286 } 287 288 public boolean exec (Event event) { 289 if (stepWatch != null) { 290 stepWatch.done(); 291 stepWatch = null; 292 } 293 JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl)debugger; 295 JPDAThreadImpl tr = (JPDAThreadImpl)debuggerImpl.getCurrentThread(); 296 VirtualMachine vm = debuggerImpl.getVirtualMachine(); 297 if (vm == null) { 298 return false; } 300 if (lastMethodExitBreakpointListener != null) { 301 Variable returnValue = lastMethodExitBreakpointListener.getReturnValue(); 302 lastMethodExitBreakpointListener.destroy(); 303 lastMethodExitBreakpointListener = null; 304 lastOperation.setReturnValue(returnValue); 305 } 306 if (lastOperation != null) { 307 tr.addLastOperation(lastOperation); 308 } 309 Operation currentOperation = null; 310 boolean addExprStep = false; 311 if (currentOperations != null) { 312 if (event.request() instanceof BreakpointRequest) { 313 long codeIndex = ((BreakpointRequest) event.request()).location().codeIndex(); 314 for (int i = 0; i < currentOperations.length; i++) { 315 if (currentOperations[i].getBytecodeIndex() == codeIndex) { 316 currentOperation = currentOperations[i]; 317 break; 318 } 319 } 320 } else { 321 addExprStep = true; 324 } 325 this.currentOperations = null; 326 } 327 tr.setCurrentOperation(currentOperation); 328 EventRequestManager erm = vm.eventRequestManager(); 329 EventRequest eventRequest = event.request(); 330 erm.deleteEventRequest(eventRequest); 331 if (eventRequest instanceof StepRequest) { 332 SingleThreadedStepWatch.stepRequestDeleted((StepRequest) eventRequest); 333 } 334 if (operationBreakpoints != null) { 335 for (Iterator <BreakpointRequest> it = operationBreakpoints.iterator(); it.hasNext(); ) { 336 erm.deleteEventRequest(it.next()); 337 } 338 this.operationBreakpoints = null; 339 } 340 if (boundaryStepRequest != null) { 341 erm.deleteEventRequest(boundaryStepRequest); 342 SingleThreadedStepWatch.stepRequestDeleted(boundaryStepRequest); 343 } 344 int suspendPolicy = debugger.getSuspend(); 345 if (addExprStep) { 346 if (addOperationStep(tr, true)) { 347 return true; } 349 } 350 if ((event.request() instanceof StepRequest) && shouldNotStopHere(event)) { 351 return true; } 353 firePropertyChange(PROP_STATE_EXEC, null, null); 354 if (! getHidden()) { 355 DebuggerManager.getDebuggerManager().setCurrentSession(session); 356 debuggerImpl.setStoppedState(tr.getThreadReference()); 357 } 358 if (getHidden()) { 359 return true; } else { 361 return false; 362 } 363 } 364 365 368 private boolean shouldNotStopHere(Event ev) { 369 JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger; 370 synchronized (debuggerImpl.LOCK) { 371 LocatableEvent event = (LocatableEvent) ev; 373 String className = event.location ().declaringType ().name (); 374 ThreadReference tr = event.thread (); 375 377 try { 379 if (tr.frame(0).location().method().isSynthetic()) { 380 382 VirtualMachine vm = debuggerImpl.getVirtualMachine (); 383 if (vm == null) { 384 return false; } 386 StepRequest stepRequest = vm.eventRequestManager ().createStepRequest ( 387 tr, 388 StepRequest.STEP_LINE, 389 getDepth() 390 ); 391 stepRequest.addCountFilter(1); 392 debuggerImpl.getOperator ().register (stepRequest, this); 393 stepRequest.setSuspendPolicy (debugger.getSuspend ()); 394 try { 395 stepRequest.enable (); 396 } catch (IllegalThreadStateException itsex) { 397 debuggerImpl.getOperator ().unregister (stepRequest); 399 } 400 return true; 401 } 402 } catch (IncompatibleThreadStateException e) { 403 ErrorManager.getDefault().notify(e); 404 } 405 406 JPDAThread t = debuggerImpl.getThread (tr); 408 if (debuggerImpl.stopHere(t)) { 409 return false; } 412 413 VirtualMachine vm = debuggerImpl.getVirtualMachine (); 415 if (vm == null) { 416 return false; } 418 StepRequest stepRequest = vm.eventRequestManager ().createStepRequest ( 419 tr, 420 StepRequest.STEP_LINE, 421 StepRequest.STEP_INTO 422 ); 423 stepRequest.addCountFilter(1); 424 debuggerImpl.getOperator ().register (stepRequest, this); 425 stepRequest.setSuspendPolicy (debugger.getSuspend ()); 426 try { 427 stepRequest.enable (); 428 } catch (IllegalThreadStateException itsex) { 429 debuggerImpl.getOperator ().unregister (stepRequest); 431 } 432 return true; } 434 } 435 436 public static final class MethodExitBreakpointListener implements JPDABreakpointListener { 437 438 private MethodBreakpoint mb; 439 private Variable returnValue; 440 441 public MethodExitBreakpointListener(MethodBreakpoint mb) { 442 this.mb = mb; 443 } 444 445 public void breakpointReached(JPDABreakpointEvent event) { 446 returnValue = event.getVariable(); 447 } 448 449 public Variable getReturnValue() { 450 return returnValue; 451 } 452 453 public void destroy() { 454 mb.removeJPDABreakpointListener(this); 455 DebuggerManager.getDebuggerManager().removeBreakpoint(mb); 456 } 457 458 } 459 460 public static final class SingleThreadedStepWatch implements Runnable { 461 462 private static final int DELAY = 5000; 463 464 private static final RequestProcessor stepWatchRP = new RequestProcessor("Debugger Step Watch", 1); 465 466 private static final Map <StepRequest, SingleThreadedStepWatch> STEP_WATCH_POOL = new HashMap <StepRequest, SingleThreadedStepWatch>(); 467 468 private RequestProcessor.Task watchTask; 469 private JPDADebuggerImpl debugger; 470 private StepRequest request; 471 private Dialog dialog; 472 private List <JPDAThread> resumedThreads; 473 474 public SingleThreadedStepWatch(JPDADebuggerImpl debugger, StepRequest request) { 475 this.debugger = debugger; 476 this.request = request; 477 watchTask = stepWatchRP.post(this, DELAY); 478 synchronized (STEP_WATCH_POOL) { 479 STEP_WATCH_POOL.put(request, this); 480 } 481 } 482 483 public static void stepRequestDeleted(StepRequest request) { 484 SingleThreadedStepWatch stepWatch; 485 synchronized (STEP_WATCH_POOL) { 486 stepWatch = STEP_WATCH_POOL.remove(request); 487 } 488 if (stepWatch != null) stepWatch.done(); 489 } 490 491 public void done() { 492 synchronized (this) { 493 watchTask.cancel(); 494 watchTask = null; 495 if (dialog != null) { 496 dialog.setVisible(false); 497 } 498 if (resumedThreads != null) { 499 synchronized (debugger.LOCK) { 500 suspendThreads(resumedThreads); 501 } 502 resumedThreads = null; 503 } 504 } 505 synchronized (STEP_WATCH_POOL) { 506 STEP_WATCH_POOL.remove(request); 507 } 508 } 509 510 public void run() { 511 synchronized (this) { 512 if (watchTask == null) return ; if (request.thread().isSuspended()) { 514 watchTask.schedule(DELAY); 515 return ; 516 } 517 } 518 String message = NbBundle.getMessage(JPDAStepImpl.class, "SingleThreadedStepBlocked"); 519 final boolean[] yes = new boolean[] { true }; 520 DialogDescriptor dd = new DialogDescriptor( 521 message, 522 new NotifyDescriptor.Confirmation(message, NotifyDescriptor.YES_NO_OPTION).getTitle(), 523 true, 524 NotifyDescriptor.YES_NO_OPTION, 525 null, 526 new ActionListener () { 527 public void actionPerformed(ActionEvent evt) { 528 synchronized (yes) { 529 yes[0] = evt.getSource() == NotifyDescriptor.YES_OPTION; 530 } 531 } 532 }); 533 dd.setMessageType(NotifyDescriptor.QUESTION_MESSAGE); 534 Dialog theDialog; 535 synchronized (this) { 536 dialog = org.openide.DialogDisplayer.getDefault().createDialog(dd); 537 theDialog = dialog; 538 } 539 theDialog.setVisible(true); 540 boolean doResume; 541 synchronized (yes) { 542 doResume = yes[0]; 543 } 544 synchronized (this) { 545 dialog = null; 546 if (watchTask == null) return ; 547 if (doResume) { 548 synchronized (debugger.LOCK) { 549 List <JPDAThread> suspendedThreads = new ArrayList <JPDAThread>(); 550 JPDAThreadGroup[] tgs = debugger.getTopLevelThreadGroups(); 551 for (JPDAThreadGroup tg: tgs) { 552 fillSuspendedThreads(tg, suspendedThreads); 553 } 554 resumeThreads(suspendedThreads); 555 resumedThreads = suspendedThreads; 556 } 557 } 558 } 559 566 } 567 568 private static void fillSuspendedThreads(JPDAThreadGroup tg, List <JPDAThread> sts) { 569 for (JPDAThread t : tg.getThreads()) { 570 if (t.isSuspended()) sts.add(t); 571 } 572 for (JPDAThreadGroup tgg : tg.getThreadGroups()) { 573 fillSuspendedThreads(tgg, sts); 574 } 575 } 576 577 private static void suspendThreads(List <JPDAThread> ts) { 578 for (JPDAThread t : ts) { 579 t.suspend(); 580 } 581 } 582 583 private static void resumeThreads(List <JPDAThread> ts) { 584 for (JPDAThread t : ts) { 585 t.resume(); 586 } 587 } 588 589 } 590 591 } 592 | Popular Tags |