mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
track classes redefinition outside of the debugger for IDEA-156500 and IDEA-182956
This commit is contained in:
@@ -6,6 +6,7 @@ import com.intellij.debugger.engine.events.DebuggerCommandImpl;
|
||||
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
|
||||
import com.intellij.debugger.engine.requests.LocatableEventRequestor;
|
||||
import com.intellij.debugger.engine.requests.MethodReturnValueWatcher;
|
||||
import com.intellij.debugger.engine.requests.RequestManagerImpl;
|
||||
import com.intellij.debugger.impl.DebuggerManagerImpl;
|
||||
import com.intellij.debugger.impl.DebuggerSession;
|
||||
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
|
||||
@@ -13,6 +14,7 @@ import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
|
||||
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.InstrumentationTracker;
|
||||
import com.intellij.debugger.ui.breakpoints.RunToCursorBreakpoint;
|
||||
import com.intellij.debugger.ui.breakpoints.StackCapturingLineBreakpoint;
|
||||
import com.intellij.debugger.ui.overhead.OverheadProducer;
|
||||
@@ -27,6 +29,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.util.ConcurrencyUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
@@ -362,6 +365,10 @@ public class DebugProcessEvents extends DebugProcessImpl {
|
||||
}
|
||||
});
|
||||
|
||||
if (Registry.is("debugger.track.instrumentation", true)) {
|
||||
trackClassRedefinitions();
|
||||
}
|
||||
|
||||
final String addressDisplayName = DebuggerBundle.getAddressDisplayName(getConnection());
|
||||
final String transportName = DebuggerBundle.getTransportName(getConnection());
|
||||
showStatusText(DebuggerBundle.message("status.connected", addressDisplayName, transportName));
|
||||
@@ -369,6 +376,19 @@ public class DebugProcessEvents extends DebugProcessImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private void trackClassRedefinitions() {
|
||||
getManagerThread().invoke(new DebuggerCommandImpl() {
|
||||
@Override
|
||||
public Priority getPriority() {
|
||||
return Priority.HIGH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void action() {
|
||||
InstrumentationTracker.track(DebugProcessEvents.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
private void createStackCapturingBreakpoints() {
|
||||
getManagerThread().invoke(new DebuggerCommandImpl() {
|
||||
@Override
|
||||
@@ -484,15 +504,16 @@ public class DebugProcessEvents extends DebugProcessImpl {
|
||||
final SuspendManager suspendManager = getSuspendManager();
|
||||
SuspendContextImpl evaluatingContext = SuspendManagerUtil.getEvaluatingContext(suspendManager, suspendContext.getThread());
|
||||
|
||||
if (evaluatingContext != null && !DebuggerSession.enableBreakpointsDuringEvaluation()) {
|
||||
final LocatableEventRequestor requestor = (LocatableEventRequestor)RequestManagerImpl.findRequestor(event.request());
|
||||
if (evaluatingContext != null &&
|
||||
!(requestor instanceof InstrumentationTracker.InstrumentationMethodBreakpoint) &&
|
||||
!DebuggerSession.enableBreakpointsDuringEvaluation()) {
|
||||
notifySkippedBreakpoints(event);
|
||||
// is inside evaluation, so ignore any breakpoints
|
||||
suspendManager.voteResume(suspendContext);
|
||||
return;
|
||||
}
|
||||
|
||||
final LocatableEventRequestor requestor = (LocatableEventRequestor) getRequestsManager().findRequestor(event.request());
|
||||
|
||||
boolean resumePreferred = requestor != null && DebuggerSettings.SUSPEND_NONE.equals(requestor.getSuspendPolicy());
|
||||
boolean requestHit = false;
|
||||
long start = requestor instanceof OverheadProducer && !(requestor instanceof RunToCursorBreakpoint) ? System.currentTimeMillis() : 0;
|
||||
|
||||
+2
-5
@@ -1,6 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
*/
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.engine.requests;
|
||||
|
||||
import com.intellij.debugger.DebuggerBundle;
|
||||
@@ -21,7 +19,6 @@ import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.ui.classFilter.ClassFilter;
|
||||
import java.util.HashMap;
|
||||
import com.sun.jdi.*;
|
||||
import com.sun.jdi.event.ClassPrepareEvent;
|
||||
import com.sun.jdi.request.*;
|
||||
@@ -73,7 +70,7 @@ public class RequestManagerImpl extends DebugProcessAdapterImpl implements Reque
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Requestor findRequestor(EventRequest request) {
|
||||
public static Requestor findRequestor(EventRequest request) {
|
||||
DebuggerManagerThreadImpl.assertIsManagerThread();
|
||||
return request != null? (Requestor)request.getProperty(REQUESTOR) : null;
|
||||
}
|
||||
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.ui.breakpoints;
|
||||
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
|
||||
import com.intellij.debugger.engine.requests.RequestManagerImpl;
|
||||
import com.intellij.debugger.requests.Requestor;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.ReflectionUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.sun.jdi.*;
|
||||
import com.sun.jdi.event.LocatableEvent;
|
||||
import com.sun.tools.jdi.ReferenceTypeImpl;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author egor
|
||||
*/
|
||||
public class InstrumentationTracker {
|
||||
private static final Logger LOG = Logger.getInstance(InstrumentationTracker.class);
|
||||
|
||||
@SuppressWarnings("FieldCanBeLocal") private final InstrumentationMethodBreakpoint myRedefineBreakpoint;
|
||||
@SuppressWarnings("FieldCanBeLocal") private final InstrumentationMethodBreakpoint myRetransformBreakpoint;
|
||||
@NotNull private final DebugProcessImpl myDebugProcess;
|
||||
|
||||
private static final java.lang.reflect.Method ourNoticeRedefineClassMethod =
|
||||
ReflectionUtil.getDeclaredMethod(ReferenceTypeImpl.class, "noticeRedefineClass");
|
||||
|
||||
public static void track(DebugProcessImpl debugProcess) {
|
||||
if (ourNoticeRedefineClassMethod != null) {
|
||||
new InstrumentationTracker(debugProcess);
|
||||
}
|
||||
}
|
||||
|
||||
private InstrumentationTracker(DebugProcessImpl debugProcess) {
|
||||
myRedefineBreakpoint =
|
||||
new InstrumentationMethodBreakpoint(debugProcess.getProject(), "sun.instrument.InstrumentationImpl", "redefineClasses") {
|
||||
@Override
|
||||
public boolean processLocatableEvent(SuspendContextCommandImpl action, LocatableEvent event) {
|
||||
try {
|
||||
Value value = event.thread().frame(0).getArgumentValues().get(0);
|
||||
if (value instanceof ArrayReference) {
|
||||
((ArrayReference)value).getValues().forEach(v -> {
|
||||
Value aClass = ((ObjectReference)v).getValue(((ReferenceType)v.type()).fieldByName("mClass"));
|
||||
noticeRedefineClass(((ClassObjectReference)aClass).reflectedType());
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (IncompatibleThreadStateException e) {
|
||||
LOG.warn(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
myRetransformBreakpoint =
|
||||
new InstrumentationMethodBreakpoint(debugProcess.getProject(), "sun.instrument.InstrumentationImpl", "retransformClasses") {
|
||||
@Override
|
||||
public boolean processLocatableEvent(SuspendContextCommandImpl action, LocatableEvent event) {
|
||||
try {
|
||||
Value value = event.thread().frame(0).getArgumentValues().get(0);
|
||||
if (value instanceof ArrayReference) {
|
||||
((ArrayReference)value).getValues().forEach(v -> noticeRedefineClass(((ClassObjectReference)v).reflectedType()));
|
||||
}
|
||||
}
|
||||
catch (IncompatibleThreadStateException e) {
|
||||
LOG.warn(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
myDebugProcess = debugProcess;
|
||||
|
||||
myRedefineBreakpoint.createRequest(debugProcess);
|
||||
myRetransformBreakpoint.createRequest(debugProcess);
|
||||
}
|
||||
|
||||
private void noticeRedefineClass(ReferenceType type) {
|
||||
List<Requestor> requestors = StreamEx.of(type.virtualMachine().eventRequestManager().breakpointRequests())
|
||||
.filter(r -> type.equals(r.location().declaringType()))
|
||||
.map(RequestManagerImpl::findRequestor)
|
||||
.toList();
|
||||
requestors.forEach(myDebugProcess.getRequestsManager()::deleteRequest);
|
||||
|
||||
try {
|
||||
//noinspection ConstantConditions
|
||||
ourNoticeRedefineClassMethod.invoke(type);
|
||||
}
|
||||
catch (IllegalAccessException | InvocationTargetException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
StreamEx.of(requestors).select(Breakpoint.class).forEach(b -> b.createRequest(myDebugProcess));
|
||||
}
|
||||
|
||||
public static class InstrumentationMethodBreakpoint extends SyntheticLineBreakpoint {
|
||||
private final String myClassName;
|
||||
private final String myMethodName;
|
||||
|
||||
public InstrumentationMethodBreakpoint(@NotNull Project project, String className, String methodName) {
|
||||
super(project);
|
||||
myClassName = className;
|
||||
myMethodName = methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createRequest(@NotNull DebugProcessImpl debugProcess) {
|
||||
createOrWaitPrepare(debugProcess, myClassName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createRequestForPreparedClass(DebugProcessImpl debugProcess, ReferenceType classType) {
|
||||
for (Method method : classType.methodsByName(myMethodName)) {
|
||||
createRequestInMethod(debugProcess, method);
|
||||
}
|
||||
}
|
||||
|
||||
protected void createRequestInMethod(DebugProcessImpl debugProcess, Method method) {
|
||||
try {
|
||||
Location location = ContainerUtil.getLastItem(method.allLineLocations());
|
||||
BreakpointWithHighlighter.createLocationBreakpointRequest(this, location, debugProcess);
|
||||
}
|
||||
catch (AbsentInformationException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Instrumentation tracker: " + myMethodName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,6 +411,7 @@ debugger.enable.overhead.monitor=true
|
||||
debugger.tree.states.depth=100
|
||||
debugger.new.threads.view=false
|
||||
debugger.breakpoints.caller.filter=true
|
||||
debugger.track.instrumentation=true
|
||||
|
||||
index.run.configuration.jre=false
|
||||
index.run.configuration.jre.restartRequired=true
|
||||
|
||||
Reference in New Issue
Block a user