mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
debugger agent prototype, for now works only in debug mode
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.rt.debugger.agent;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author egor
|
||||
*/
|
||||
public class CaptureStorage {
|
||||
private static final int MAX_STORED_STACKS = 1000;
|
||||
|
||||
public static final Map<Object, Exception> STORAGE = Collections.synchronizedMap(new LinkedHashMap<Object, Exception>() {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry eldest) {
|
||||
return size() > MAX_STORED_STACKS;
|
||||
}
|
||||
});
|
||||
|
||||
// to be run from the debugger
|
||||
@SuppressWarnings("unused")
|
||||
public static Object[][] getRelatedStack(Object key) {
|
||||
Exception exception = STORAGE.get(key);
|
||||
StackTraceElement[] stackTrace = exception.getStackTrace();
|
||||
Object[][] res = new Object[stackTrace.length][];
|
||||
for (int i = 0; i < stackTrace.length; i++) {
|
||||
StackTraceElement elem = stackTrace[i];
|
||||
res[i] = new Object[]{elem.getClassName(), elem.getFileName(), elem.getMethodName(), String.valueOf(elem.getLineNumber())};
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
Manifest-Version: 1.0
|
||||
Premain-Class: com.intellij.rt.debugger.agent.CaptureAgent
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="ASM" level="project" />
|
||||
<orderEntry type="module" module-name="debugger-agent-storage" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.rt.debugger.agent;
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.instrument.UnmodifiableClassException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* @author egor
|
||||
*/
|
||||
public class CaptureAgent {
|
||||
private static Instrumentation OurInstrumentation;
|
||||
private static volatile List<CapturePoint> myCapturePoints = Arrays.asList(new CapturePoint
|
||||
("javax/swing/SwingUtilities", "invokeLater", 0)
|
||||
// ,
|
||||
// new CapturePoint("Test", "foo")
|
||||
);
|
||||
|
||||
public static void premain(String args, Instrumentation instrumentation) throws IOException {
|
||||
OurInstrumentation = instrumentation;
|
||||
instrumentation.appendToBootstrapClassLoaderSearch(createTempJar("debugger-agent-storage.jar"));
|
||||
instrumentation.appendToSystemClassLoaderSearch(createTempJar("asm-all.jar"));
|
||||
instrumentation.addTransformer(new CaptureTransformer());
|
||||
System.out.println("Capture agent: ready");
|
||||
}
|
||||
|
||||
private static class CaptureTransformer implements ClassFileTransformer {
|
||||
@Override
|
||||
public byte[] transform(ClassLoader loader,
|
||||
String className,
|
||||
Class<?> classBeingRedefined,
|
||||
ProtectionDomain protectionDomain,
|
||||
byte[] classfileBuffer) {
|
||||
// TODO: speedup
|
||||
for (CapturePoint capturePoint : myCapturePoints) {
|
||||
if (capturePoint.myClassName.equals(className)) {
|
||||
ClassReader reader = new ClassReader(classfileBuffer);
|
||||
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
|
||||
CaptureInstrumentor visitor = new CaptureInstrumentor(Opcodes.ASM6, writer, capturePoint);
|
||||
reader.accept(visitor, 0);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CaptureInstrumentor extends ClassVisitor {
|
||||
private CapturePoint capturePoint;
|
||||
|
||||
public CaptureInstrumentor(int api, ClassVisitor cv, CapturePoint capturePoint) {
|
||||
super(api, cv);
|
||||
this.capturePoint = capturePoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
|
||||
if (capturePoint.myMethodName.equals(name)) {
|
||||
System.out.println("Capture agent: instrumented " + capturePoint.myClassName + "." + name);
|
||||
return new MethodVisitor(api, mv) {
|
||||
@Override
|
||||
public void visitCode() {
|
||||
visitFieldInsn(Opcodes.GETSTATIC, CaptureStorage.class.getName().replaceAll("\\.", "/"), "STORAGE", "Ljava/util/Map;");
|
||||
visitVarInsn(Opcodes.ALOAD, capturePoint.myParamSlotId);
|
||||
visitTypeInsn(Opcodes.NEW, "java/lang/Exception");
|
||||
visitInsn(Opcodes.DUP);
|
||||
visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Exception", "<init>", "()V", false);
|
||||
visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/Map", "put",
|
||||
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
|
||||
visitInsn(Opcodes.POP);
|
||||
super.visitCode();
|
||||
}
|
||||
};
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
}
|
||||
|
||||
static class CapturePoint {
|
||||
final String myClassName;
|
||||
final String myMethodName;
|
||||
final int myParamSlotId;
|
||||
|
||||
public CapturePoint(String myClassName, String myMethodName, int myParamSlotId) {
|
||||
this.myClassName = myClassName;
|
||||
this.myMethodName = myMethodName;
|
||||
this.myParamSlotId = myParamSlotId;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: these files are not deleted even if deleteOnExit or anything else, we need to separate jars
|
||||
private static JarFile createTempJar(String name) throws IOException {
|
||||
File tempJar = File.createTempFile("Capture", ".jar");
|
||||
Files.copy(CaptureAgent.class.getClassLoader().getResourceAsStream(name), tempJar.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING);
|
||||
JarFile res = new JarFile(tempJar);
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
res.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
tempJar.delete();
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
// to be run from the debugger
|
||||
@SuppressWarnings("unused")
|
||||
public static void setCapturePoints(Object[][] capturePoints) throws UnmodifiableClassException {
|
||||
Set<String> classNames = new HashSet<>();
|
||||
for (CapturePoint point : myCapturePoints) {
|
||||
classNames.add(point.myClassName);
|
||||
}
|
||||
|
||||
List<CapturePoint> points = new ArrayList<>(capturePoints.length);
|
||||
for (Object[] capturePoint : capturePoints) {
|
||||
String className = (String)capturePoint[0];
|
||||
classNames.add(className);
|
||||
points.add(new CapturePoint(className, (String)capturePoint[1], (int)capturePoint[2]));
|
||||
}
|
||||
myCapturePoints = points;
|
||||
|
||||
List<Class> classes = new ArrayList<>(capturePoints.length);
|
||||
for (String name : classNames) {
|
||||
try {
|
||||
classes.add(Class.forName(name));
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
OurInstrumentation.retransformClasses(classes.toArray(new Class[0]));
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
<orderEntry type="module" module-name="platform-impl" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="module" module-name="jps-builders" />
|
||||
<orderEntry type="module" module-name="debugger-agent-storage" />
|
||||
</component>
|
||||
<component name="copyright">
|
||||
<Base>
|
||||
|
||||
@@ -208,7 +208,7 @@ public class JavaExecutionStack extends XExecutionStack {
|
||||
|
||||
// replace the rest with the related stack (if available)
|
||||
if (Registry.is("debugger.capture.points") && frame instanceof JavaStackFrame) {
|
||||
List<StackFrameItem> relatedStack = StackCapturingLineBreakpoint.getRelatedStack(frameProxy, suspendContext);
|
||||
List<StackFrameItem> relatedStack = StackCapturingLineBreakpoint.getRelatedStack(frameProxy, suspendContext, true);
|
||||
if (!ContainerUtil.isEmpty(relatedStack)) {
|
||||
int i = 0;
|
||||
boolean separator = true;
|
||||
|
||||
@@ -49,11 +49,15 @@ import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
|
||||
import com.intellij.openapi.startup.StartupManager;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.WriteExternalException;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.rt.debugger.agent.CaptureStorage;
|
||||
import com.intellij.util.EventDispatcher;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.PathUtil;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jdom.Element;
|
||||
@@ -466,6 +470,16 @@ public class DebuggerManagerImpl extends DebuggerManagerEx implements Persistent
|
||||
|
||||
ApplicationManager.getApplication().runReadAction(() -> {
|
||||
JavaSdkUtil.addRtJar(parameters.getClassPath());
|
||||
if (Registry.is("debugger.capture.points.agent")) {
|
||||
String path = PathUtil.getJarPathForClass(CaptureStorage.class);
|
||||
//TODO: for now works only in debug mode
|
||||
String agent = "-javaagent:" +
|
||||
FileUtil.toSystemDependentName(PathUtil.getParentPath(PathUtil.getParentPath(path))) +
|
||||
"/artifacts/debugger_agent/debugger-agent.jar";
|
||||
if (!parameters.getVMParametersList().hasParameter(agent)) {
|
||||
parameters.getVMParametersList().add(agent);
|
||||
}
|
||||
}
|
||||
|
||||
final Sdk jdk = parameters.getJdk();
|
||||
final boolean forceClassicVM = shouldForceClassicVM(jdk);
|
||||
|
||||
@@ -26,17 +26,20 @@ import com.intellij.debugger.settings.NodeRendererSettings;
|
||||
import com.intellij.debugger.ui.breakpoints.StackCapturingLineBreakpoint;
|
||||
import com.intellij.debugger.ui.tree.render.ClassRenderer;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.options.ShowSettingsUtil;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.ui.ColoredTextContainer;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.EmptyIcon;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.xdebugger.XDebuggerUtil;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.intellij.xdebugger.frame.*;
|
||||
import com.intellij.xdebugger.frame.presentation.XStringValuePresentation;
|
||||
@@ -82,6 +85,11 @@ public class StackFrameItem {
|
||||
return myLocation.declaringType().name();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String method() {
|
||||
return myLocation.method().name();
|
||||
}
|
||||
|
||||
public int line() {
|
||||
return DebuggerUtilsEx.getLineNumber(myLocation, false);
|
||||
}
|
||||
@@ -152,7 +160,7 @@ public class StackFrameItem {
|
||||
StackFrameItem frameItem = new StackFrameItem(location, vars);
|
||||
res.add(frameItem);
|
||||
|
||||
List<StackFrameItem> relatedStack = StackCapturingLineBreakpoint.getRelatedStack(frame, suspendContext);
|
||||
List<StackFrameItem> relatedStack = StackCapturingLineBreakpoint.getRelatedStack(frame, suspendContext, false);
|
||||
if (!ContainerUtil.isEmpty(relatedStack)) {
|
||||
res.add(null); // separator
|
||||
res.addAll(relatedStack);
|
||||
@@ -239,14 +247,27 @@ public class StackFrameItem {
|
||||
|
||||
public CapturedStackFrame(DebugProcessImpl debugProcess, StackFrameItem item) {
|
||||
DebuggerManagerThreadImpl.assertIsManagerThread();
|
||||
mySourcePosition = DebuggerUtilsEx.toXSourcePosition(debugProcess.getPositionManager().getSourcePosition(item.myLocation));
|
||||
myIsSynthetic = DebuggerUtils.isSynthetic(item.myLocation.method());
|
||||
myIsInLibraryContent =
|
||||
DebuggerUtilsEx.isInLibraryContent(mySourcePosition != null ? mySourcePosition.getFile() : null, debugProcess.getProject());
|
||||
myPath = item.path();
|
||||
myMethodName = item.myLocation.method().name();
|
||||
myMethodName = item.method();
|
||||
myLineNumber = item.line();
|
||||
myVariables = item.myVariables;
|
||||
|
||||
Location location = item.myLocation;
|
||||
if (location != null) {
|
||||
mySourcePosition = DebuggerUtilsEx.toXSourcePosition(debugProcess.getPositionManager().getSourcePosition(location));
|
||||
}
|
||||
else {
|
||||
mySourcePosition = ReadAction.compute(() -> {
|
||||
PsiClass aClass = PositionManagerImpl.findClass(debugProcess.getProject(), item.path(), debugProcess.getSearchScope());
|
||||
if (aClass != null) {
|
||||
return XDebuggerUtil.getInstance().createPosition(aClass.getContainingFile().getVirtualFile(), myLineNumber - 1);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
myIsSynthetic = location != null && DebuggerUtils.isSynthetic(location.method());
|
||||
myIsInLibraryContent =
|
||||
DebuggerUtilsEx.isInLibraryContent(mySourcePosition != null ? mySourcePosition.getFile() : null, debugProcess.getProject());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+73
-8
@@ -39,6 +39,7 @@ import com.intellij.openapi.util.ThrowableComputable;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.rt.debugger.agent.CaptureStorage;
|
||||
import com.intellij.ui.SimpleColoredComponent;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.sun.jdi.*;
|
||||
@@ -48,10 +49,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.java.debugger.breakpoints.properties.JavaMethodBreakpointProperties;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
@@ -245,10 +243,12 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static List<StackFrameItem> getRelatedStack(@NotNull StackFrameProxyImpl frame, @NotNull SuspendContextImpl suspendContext) {
|
||||
public static List<StackFrameItem> getRelatedStack(@NotNull StackFrameProxyImpl frame,
|
||||
@NotNull SuspendContextImpl suspendContext,
|
||||
boolean checkInProcessData) {
|
||||
DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
|
||||
Map<Object, List<StackFrameItem>> capturedStacks = debugProcess.getUserData(CAPTURED_STACKS);
|
||||
if (ContainerUtil.isEmpty(capturedStacks)) {
|
||||
if (ContainerUtil.isEmpty(capturedStacks) && !Registry.is("debugger.capture.points.agent")) {
|
||||
return null;
|
||||
}
|
||||
List<StackCapturingLineBreakpoint> captureBreakpoints = debugProcess.getUserData(CAPTURE_BREAKPOINTS);
|
||||
@@ -265,10 +265,18 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
|
||||
if ((StringUtil.isEmpty(insertClassName) || StringUtil.equals(insertClassName, className)) &&
|
||||
StringUtil.equals(b.myCapturePoint.myInsertMethodName, methodName)) {
|
||||
try {
|
||||
Value key = b.myInsertEvaluator.evaluate(new EvaluationContextImpl(suspendContext, frame));
|
||||
EvaluationContextImpl evaluationContext = new EvaluationContextImpl(suspendContext, frame);
|
||||
Value key = b.myInsertEvaluator.evaluate(evaluationContext);
|
||||
List<StackFrameItem> items = null;
|
||||
if (key instanceof ObjectReference) {
|
||||
return capturedStacks.get(getKey((ObjectReference)key));
|
||||
if (capturedStacks != null) {
|
||||
items = capturedStacks.get(getKey((ObjectReference)key));
|
||||
}
|
||||
if (items == null && checkInProcessData) {
|
||||
items = getProcessCapturedStack(key, evaluationContext);
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.debug(e);
|
||||
@@ -283,6 +291,63 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<StackFrameItem> getProcessCapturedStack(Value key, EvaluationContextImpl evaluationContext)
|
||||
throws EvaluateException {
|
||||
if (Registry.is("debugger.capture.points.agent")) {
|
||||
DebugProcessImpl process = evaluationContext.getDebugProcess();
|
||||
// TODO: cache class & method
|
||||
ClassType captureClass = (ClassType)process.findClass(evaluationContext, CaptureStorage.class.getName(), null);
|
||||
Method getRelatedStackMethod = captureClass.methodsByName("getRelatedStack").get(0);
|
||||
Value resArray = process.invokeMethod(evaluationContext, captureClass, getRelatedStackMethod, Collections.singletonList(key), true);
|
||||
if (resArray instanceof ArrayReference) {
|
||||
List<Value> values = ((ArrayReference)resArray).getValues();
|
||||
List<StackFrameItem> res = new ArrayList<>(values.size());
|
||||
for (Value value : values) {
|
||||
List<Value> values1 = ((ArrayReference)value).getValues();
|
||||
res.add(new ProcessStackFrameItem(getStringRefValue((StringReference)values1.get(0)),
|
||||
getStringRefValue((StringReference)values1.get(2)),
|
||||
Integer.parseInt(((StringReference)values1.get(3)).value())));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getStringRefValue(StringReference ref) {
|
||||
return ref != null ? ref.value() : null;
|
||||
}
|
||||
|
||||
private static class ProcessStackFrameItem extends StackFrameItem {
|
||||
final String myClass;
|
||||
final String myMethod;
|
||||
final int myLine;
|
||||
|
||||
public ProcessStackFrameItem(String aClass, String method, int line) {
|
||||
super(null, null);
|
||||
myClass = aClass;
|
||||
myMethod = method;
|
||||
myLine = line;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String path() {
|
||||
return myClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int line() {
|
||||
return myLine;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String method() {
|
||||
return myMethod;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static List<StackFrameItem> getRelatedStack(@Nullable ObjectReference key, @Nullable DebugProcessImpl process) {
|
||||
if (process != null && key != null) {
|
||||
|
||||
@@ -297,6 +297,7 @@ debugger.emulate.method.breakpoints.description=Emulate method breakpoints with
|
||||
debugger.intern.string.literals=false
|
||||
debugger.intern.string.literals.description=Make string literal refer to the same instance of class String
|
||||
debugger.capture.points=true
|
||||
debugger.capture.points.agent=false
|
||||
debugger.capture.points.annotations=false
|
||||
debugger.resume.yourkit.threads=false
|
||||
debugger.keep.step.requests=false
|
||||
|
||||
Reference in New Issue
Block a user