| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.debugger.engine.requests; |
| |
| import com.intellij.debugger.DebuggerBundle; |
| import com.intellij.debugger.DebuggerManagerEx; |
| import com.intellij.debugger.SourcePosition; |
| import com.intellij.debugger.engine.*; |
| import com.intellij.debugger.engine.evaluation.EvaluateException; |
| import com.intellij.debugger.engine.events.DebuggerCommandImpl; |
| import com.intellij.debugger.impl.DebuggerSession; |
| import com.intellij.debugger.impl.DebuggerUtilsEx; |
| import com.intellij.debugger.requests.ClassPrepareRequestor; |
| import com.intellij.debugger.requests.RequestManager; |
| import com.intellij.debugger.requests.Requestor; |
| import com.intellij.debugger.settings.DebuggerSettings; |
| import com.intellij.debugger.ui.breakpoints.Breakpoint; |
| import com.intellij.debugger.ui.breakpoints.BreakpointManager; |
| import com.intellij.debugger.ui.breakpoints.FilteredRequestor; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.psi.PsiClass; |
| import com.intellij.ui.classFilter.ClassFilter; |
| import com.intellij.util.containers.HashMap; |
| import com.sun.jdi.*; |
| import com.sun.jdi.event.ClassPrepareEvent; |
| import com.sun.jdi.request.*; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * @author lex |
| * Date: May 6, 2003 |
| * Time: 5:32:38 PM |
| */ |
| public class RequestManagerImpl extends DebugProcessAdapterImpl implements RequestManager { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.RequestManagerImpl"); |
| |
| private static final Key CLASS_NAME = Key.create("ClassName"); |
| private static final Key<Requestor> REQUESTOR = Key.create("Requestor"); |
| |
| private final DebugProcessImpl myDebugProcess; |
| private final Map<Requestor, String> myRequestWarnings = new HashMap<Requestor, String>(); |
| |
| private final Map<Requestor, Set<EventRequest>> myRequestorToBelongedRequests = new HashMap<Requestor, Set<EventRequest>>(); |
| private EventRequestManager myEventRequestManager; |
| private @Nullable ThreadReference myFilterThread; |
| |
| public RequestManagerImpl(DebugProcessImpl debugProcess) { |
| myDebugProcess = debugProcess; |
| myDebugProcess.addDebugProcessListener(this); |
| } |
| |
| |
| public EventRequestManager getVMRequestManager() { |
| return myEventRequestManager; |
| } |
| |
| @Nullable |
| public ThreadReference getFilterThread() { |
| return myFilterThread; |
| } |
| |
| public void setFilterThread(@Nullable final ThreadReference filterThread) { |
| myFilterThread = filterThread; |
| } |
| |
| public Set<EventRequest> findRequests(Requestor requestor) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| final Set<EventRequest> requestSet = myRequestorToBelongedRequests.get(requestor); |
| if (requestSet == null) { |
| return Collections.emptySet(); |
| } |
| return Collections.unmodifiableSet(requestSet); |
| } |
| |
| public Requestor findRequestor(EventRequest request) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| return request != null? (Requestor)request.getProperty(REQUESTOR) : null; |
| } |
| |
| private static void addClassFilter(EventRequest request, String pattern){ |
| if(request instanceof AccessWatchpointRequest){ |
| ((AccessWatchpointRequest) request).addClassFilter(pattern); |
| } |
| else if(request instanceof ExceptionRequest){ |
| ((ExceptionRequest) request).addClassFilter(pattern); |
| } |
| else if(request instanceof MethodEntryRequest) { |
| ((MethodEntryRequest)request).addClassFilter(pattern); |
| } |
| else if(request instanceof MethodExitRequest) { |
| ((MethodExitRequest)request).addClassFilter(pattern); |
| } |
| else if(request instanceof ModificationWatchpointRequest) { |
| ((ModificationWatchpointRequest)request).addClassFilter(pattern); |
| } |
| else if(request instanceof WatchpointRequest) { |
| ((WatchpointRequest)request).addClassFilter(pattern); |
| } |
| } |
| |
| private static void addClassExclusionFilter(EventRequest request, String pattern){ |
| if(request instanceof AccessWatchpointRequest){ |
| ((AccessWatchpointRequest) request).addClassExclusionFilter(pattern); |
| } |
| else if(request instanceof ExceptionRequest){ |
| ((ExceptionRequest) request).addClassExclusionFilter(pattern); |
| } |
| else if(request instanceof MethodEntryRequest) { |
| ((MethodEntryRequest)request).addClassExclusionFilter(pattern); |
| } |
| else if(request instanceof MethodExitRequest) { |
| ((MethodExitRequest)request).addClassExclusionFilter(pattern); |
| } |
| else if(request instanceof ModificationWatchpointRequest) { |
| ((ModificationWatchpointRequest)request).addClassExclusionFilter(pattern); |
| } |
| else if(request instanceof WatchpointRequest) { |
| ((WatchpointRequest)request).addClassExclusionFilter(pattern); |
| } |
| } |
| |
| private void addLocatableRequest(FilteredRequestor requestor, EventRequest request) { |
| if(DebuggerSettings.SUSPEND_ALL.equals(requestor.getSuspendPolicy())) { |
| request.setSuspendPolicy(EventRequest.SUSPEND_ALL); |
| } |
| else { |
| //when requestor.SUSPEND_POLICY == SUSPEND_NONE |
| //we should pause thread in order to evaluate conditions |
| request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); |
| } |
| |
| if (requestor.COUNT_FILTER_ENABLED && requestor.COUNT_FILTER > 0) { |
| request.addCountFilter(requestor.COUNT_FILTER); |
| } |
| |
| if (requestor.CLASS_FILTERS_ENABLED && !(request instanceof BreakpointRequest) /*no built-in class filters support for breakpoint requests*/ ) { |
| ClassFilter[] classFilters = requestor.getClassFilters(); |
| for (final ClassFilter filter : classFilters) { |
| if (!filter.isEnabled()) { |
| continue; |
| } |
| final JVMName jvmClassName = ApplicationManager.getApplication().runReadAction(new Computable<JVMName>() { |
| public JVMName compute() { |
| PsiClass psiClass = |
| DebuggerUtilsEx.findClass(filter.getPattern(), myDebugProcess.getProject(), myDebugProcess.getSearchScope()); |
| if (psiClass == null) { |
| return null; |
| } |
| return JVMNameUtil.getJVMQualifiedName(psiClass); |
| } |
| }); |
| String pattern = filter.getPattern(); |
| try { |
| if (jvmClassName != null) { |
| pattern = jvmClassName.getName(myDebugProcess); |
| } |
| } |
| catch (EvaluateException e) { |
| } |
| |
| addClassFilter(request, pattern); |
| } |
| |
| final ClassFilter[] iclassFilters = requestor.getClassExclusionFilters(); |
| for (ClassFilter filter : iclassFilters) { |
| if (filter.isEnabled()) { |
| addClassExclusionFilter(request, filter.getPattern()); |
| } |
| } |
| } |
| |
| registerRequestInternal(requestor, request); |
| } |
| |
| public void registerRequestInternal(final Requestor requestor, final EventRequest request) { |
| registerRequest(requestor, request); |
| request.putProperty(REQUESTOR, requestor); |
| } |
| |
| private void registerRequest(Requestor requestor, EventRequest request) { |
| Set<EventRequest> reqSet = myRequestorToBelongedRequests.get(requestor); |
| if(reqSet == null) { |
| reqSet = new HashSet<EventRequest>(); |
| myRequestorToBelongedRequests.put(requestor, reqSet); |
| } |
| reqSet.add(request); |
| |
| } |
| |
| // requests creation |
| public ClassPrepareRequest createClassPrepareRequest(ClassPrepareRequestor requestor, String pattern) { |
| ClassPrepareRequest classPrepareRequest = myEventRequestManager.createClassPrepareRequest(); |
| classPrepareRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); |
| classPrepareRequest.addClassFilter(pattern); |
| classPrepareRequest.putProperty(CLASS_NAME, pattern); |
| |
| registerRequestInternal(requestor, classPrepareRequest); |
| return classPrepareRequest; |
| } |
| |
| public ExceptionRequest createExceptionRequest(FilteredRequestor requestor, ReferenceType referenceType, boolean notifyCaught, boolean notifyUnCaught) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| ExceptionRequest req = myEventRequestManager.createExceptionRequest(referenceType, notifyCaught, notifyUnCaught); |
| addLocatableRequest(requestor, req); |
| return req; |
| } |
| |
| public MethodEntryRequest createMethodEntryRequest(FilteredRequestor requestor) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| MethodEntryRequest req = myEventRequestManager.createMethodEntryRequest(); |
| addLocatableRequest(requestor, req); |
| return req; |
| } |
| |
| public MethodExitRequest createMethodExitRequest(FilteredRequestor requestor) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| MethodExitRequest req = myEventRequestManager.createMethodExitRequest(); |
| addLocatableRequest(requestor, req); |
| return req; |
| } |
| |
| public BreakpointRequest createBreakpointRequest(FilteredRequestor requestor, Location location) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| BreakpointRequest req = myEventRequestManager.createBreakpointRequest(location); |
| addLocatableRequest(requestor, req); |
| myRequestWarnings.remove(requestor); |
| return req; |
| } |
| |
| public AccessWatchpointRequest createAccessWatchpointRequest(FilteredRequestor requestor, Field field) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| AccessWatchpointRequest req = myEventRequestManager.createAccessWatchpointRequest(field); |
| addLocatableRequest(requestor, req); |
| return req; |
| } |
| |
| public ModificationWatchpointRequest createModificationWatchpointRequest(FilteredRequestor requestor, Field field) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| ModificationWatchpointRequest req = myEventRequestManager.createModificationWatchpointRequest(field); |
| addLocatableRequest(requestor, req); |
| return req; |
| } |
| |
| public void deleteRequest(Requestor requestor) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| if(!myDebugProcess.isAttached()) { |
| return; |
| } |
| final Set<EventRequest> requests = myRequestorToBelongedRequests.remove(requestor); |
| if(requests == null) { |
| return; |
| } |
| for (final EventRequest request : requests) { |
| try { |
| final Requestor targetRequestor = (Requestor)request.getProperty(REQUESTOR); |
| if (targetRequestor != requestor) { |
| // the same request may be assigned to more than one requestor, but |
| // there is only one 'targetRequestor' for each request, so if target requestor and requestor being processed are different, |
| // should clear also the mapping targetRequestor->request |
| final Set<EventRequest> allTargetRequestorRequests = myRequestorToBelongedRequests.get(targetRequestor); |
| if (allTargetRequestorRequests != null) { |
| allTargetRequestorRequests.remove(request); |
| if (allTargetRequestorRequests.size() == 0) { |
| myRequestorToBelongedRequests.remove(targetRequestor); |
| } |
| } |
| } |
| myEventRequestManager.deleteEventRequest(request); |
| } |
| catch (InvalidRequestStateException ignored) { |
| // request is already deleted |
| } |
| catch (InternalException e) { |
| if (e.errorCode() == 41) { |
| //event request not found |
| //there could be no requests after hotswap |
| } |
| else { |
| LOG.error(e); |
| } |
| } |
| } |
| } |
| |
| public void callbackOnPrepareClasses(final ClassPrepareRequestor requestor, final SourcePosition classPosition) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| ClassPrepareRequest prepareRequest = myDebugProcess.getPositionManager().createPrepareRequest(requestor, classPosition); |
| |
| if(prepareRequest == null) { |
| setInvalid(requestor, DebuggerBundle.message("status.invalid.breakpoint.out.of.class")); |
| return; |
| } |
| |
| registerRequest(requestor, prepareRequest); |
| prepareRequest.enable(); |
| } |
| |
| public void callbackOnPrepareClasses(ClassPrepareRequestor requestor, String classOrPatternToBeLoaded) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| ClassPrepareRequest classPrepareRequest = createClassPrepareRequest(requestor, classOrPatternToBeLoaded); |
| |
| registerRequest(requestor, classPrepareRequest); |
| classPrepareRequest.enable(); |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("classOrPatternToBeLoaded = " + classOrPatternToBeLoaded); |
| } |
| } |
| |
| public void enableRequest(EventRequest request) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| LOG.assertTrue(findRequestor(request) != null); |
| try { |
| final ThreadReference filterThread = myFilterThread; |
| if (filterThread != null) { |
| if (request instanceof BreakpointRequest) { |
| ((BreakpointRequest)request).addThreadFilter(filterThread); |
| } |
| else if (request instanceof MethodEntryRequest) { |
| ((MethodEntryRequest)request).addThreadFilter(filterThread); |
| } |
| else if (request instanceof MethodExitRequest) { |
| ((MethodExitRequest)request).addThreadFilter(filterThread); |
| } |
| } |
| request.enable(); |
| } catch (InternalException e) { |
| if(e.errorCode() == 41) { |
| //event request not found |
| //there could be no requests after hotswap |
| } else { |
| LOG.error(e); |
| } |
| } |
| } |
| |
| public void setInvalid(Requestor requestor, String message) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| //deleteRequest(requestor); |
| //myRequestorToBelongedRequests.remove(requestor); // clear any mapping to empty set if any |
| if (!isVerified(requestor)) { |
| myRequestWarnings.put(requestor, message); |
| } |
| } |
| |
| public @Nullable String getWarning(Requestor requestor) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| return myRequestWarnings.get(requestor); |
| } |
| |
| public boolean isVerified(Requestor requestor) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| for (EventRequest request : findRequests(requestor)) { |
| /*ClassPrepareRequest is added in any case, so do not count it*/ |
| if (!(request instanceof ClassPrepareRequest)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public void processDetached(DebugProcessImpl process, boolean closedByUser) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| myEventRequestManager = null; |
| myRequestWarnings.clear(); |
| myRequestorToBelongedRequests.clear(); |
| } |
| |
| public void processAttached(DebugProcessImpl process) { |
| myEventRequestManager = myDebugProcess.getVirtualMachineProxy().eventRequestManager(); |
| // invoke later, so that requests are for sure created only _after_ 'processAttached()' methods of other listeneres are executed |
| process.getManagerThread().schedule(new DebuggerCommandImpl() { |
| protected void action() throws Exception { |
| final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myDebugProcess.getProject()).getBreakpointManager(); |
| for (final Breakpoint breakpoint : breakpointManager.getBreakpoints()) { |
| breakpoint.createRequest(myDebugProcess); |
| } |
| } |
| }); |
| } |
| |
| public void processClassPrepared(final ClassPrepareEvent event) { |
| if (!myDebugProcess.isAttached()) { |
| return; |
| } |
| |
| final ReferenceType refType = event.referenceType(); |
| |
| if (refType instanceof ClassType || refType instanceof InterfaceType) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("signature = " + refType.signature()); |
| } |
| ClassPrepareRequestor requestor = (ClassPrepareRequestor)event.request().getProperty(REQUESTOR); |
| if (requestor != null) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("requestor found " + refType.signature()); |
| } |
| requestor.processClassPrepare(myDebugProcess, refType); |
| } |
| } |
| } |
| |
| private static interface AllProcessesCommand { |
| void action(DebugProcessImpl process); |
| } |
| |
| private static void invoke(Project project, final AllProcessesCommand command) { |
| for (DebuggerSession debuggerSession : (DebuggerManagerEx.getInstanceEx(project)).getSessions()) { |
| final DebugProcessImpl process = debuggerSession.getProcess(); |
| if (process != null) { |
| process.getManagerThread().invoke(new DebuggerCommandImpl() { |
| protected void action() throws Exception { |
| command.action(process); |
| } |
| }); |
| } |
| } |
| } |
| |
| public static void createRequests(final Breakpoint breakpoint) { |
| invoke(breakpoint.getProject(), new AllProcessesCommand (){ |
| public void action(DebugProcessImpl process) { |
| breakpoint.createRequest(process); |
| } |
| }); |
| } |
| |
| public static void updateRequests(final Breakpoint breakpoint) { |
| invoke(breakpoint.getProject(), new AllProcessesCommand (){ |
| public void action(DebugProcessImpl process) { |
| process.getRequestsManager().myRequestWarnings.remove(breakpoint); |
| process.getRequestsManager().deleteRequest(breakpoint); |
| breakpoint.createRequest(process); |
| } |
| }); |
| } |
| |
| public static void deleteRequests(final Breakpoint breakpoint) { |
| invoke(breakpoint.getProject(), new AllProcessesCommand (){ |
| public void action(DebugProcessImpl process) { |
| process.getRequestsManager().myRequestWarnings.remove(breakpoint); |
| process.getRequestsManager().deleteRequest(breakpoint); |
| } |
| }); |
| } |
| |
| public void clearWarnings() { |
| myRequestWarnings.clear(); |
| } |
| } |