mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
prototype of a call tracer
This commit is contained in:
@@ -299,12 +299,16 @@ public class DebugProcessEvents extends DebugProcessImpl {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void enableNonSuspendingRequest(EventRequest request, Consumer<Event> handler) {
|
||||
request.setSuspendPolicy(EventRequest.SUSPEND_NONE);
|
||||
public static void enableRequestWithHandler(EventRequest request, Consumer<Event> handler) {
|
||||
request.putProperty(REQUEST_HANDLER, handler);
|
||||
request.enable();
|
||||
}
|
||||
|
||||
private static void enableNonSuspendingRequest(EventRequest request, Consumer<Event> handler) {
|
||||
request.setSuspendPolicy(EventRequest.SUSPEND_NONE);
|
||||
enableRequestWithHandler(request, handler);
|
||||
}
|
||||
|
||||
private void processVMStartEvent(final SuspendContextImpl suspendContext, VMStartEvent event) {
|
||||
preprocessEvent(suspendContext, event.thread());
|
||||
|
||||
|
||||
+35
-32
@@ -121,43 +121,46 @@ public class RequestManagerImpl extends DebugProcessAdapterImpl implements Reque
|
||||
}
|
||||
|
||||
if (requestor.isClassFiltersEnabled() && !(request instanceof BreakpointRequest) /*no built-in class filters support for breakpoint requests*/ ) {
|
||||
ClassFilter[] classFilters = requestor.getClassFilters();
|
||||
if (DebuggerUtilsEx.getEnabledNumber(classFilters) == 1) {
|
||||
for (final ClassFilter filter : classFilters) {
|
||||
if (!filter.isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
final JVMName jvmClassName = ReadAction.compute(() -> {
|
||||
PsiClass psiClass = DebuggerUtils.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 ignored) {
|
||||
}
|
||||
|
||||
addClassFilter(request, pattern);
|
||||
break; // adding more than one inclusion filter does not work, only events that satisfy ALL filters are placed in the event queue.
|
||||
}
|
||||
}
|
||||
|
||||
for (ClassFilter filter : requestor.getClassExclusionFilters()) {
|
||||
if (filter.isEnabled()) {
|
||||
addClassExclusionFilter(request, filter.getPattern());
|
||||
}
|
||||
}
|
||||
addClassFilters(request, requestor.getClassFilters(), requestor.getClassExclusionFilters());
|
||||
}
|
||||
|
||||
registerRequestInternal(requestor, request);
|
||||
}
|
||||
|
||||
public void addClassFilters(EventRequest request, ClassFilter[] classFilters, ClassFilter[] classExclusionFilters) {
|
||||
if (DebuggerUtilsEx.getEnabledNumber(classFilters) == 1) {
|
||||
for (final ClassFilter filter : classFilters) {
|
||||
if (!filter.isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
final JVMName jvmClassName = ReadAction.compute(() -> {
|
||||
PsiClass psiClass = DebuggerUtils.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 ignored) {
|
||||
}
|
||||
|
||||
addClassFilter(request, pattern);
|
||||
break; // adding more than one inclusion filter does not work, only events that satisfy ALL filters are placed in the event queue.
|
||||
}
|
||||
}
|
||||
|
||||
for (ClassFilter filter : classExclusionFilters) {
|
||||
if (filter.isEnabled()) {
|
||||
addClassExclusionFilter(request, filter.getPattern());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void registerRequestInternal(final Requestor requestor, final EventRequest request) {
|
||||
registerRequest(requestor, request);
|
||||
request.putProperty(REQUESTOR, requestor);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// 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.settings;
|
||||
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.ui.classFilter.ClassFilter;
|
||||
import com.intellij.util.xmlb.annotations.XCollection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@State(name = "TraceSettings", storages = @Storage("debugger.xml"))
|
||||
public class TraceSettings implements PersistentStateComponent<TraceSettings> {
|
||||
private ClassFilter[] myClassFilters;
|
||||
private ClassFilter[] myClassExclusionFilters;
|
||||
|
||||
@XCollection(propertyElementName = "class-filters")
|
||||
public final ClassFilter[] getClassFilters() {
|
||||
return myClassFilters != null ? myClassFilters : ClassFilter.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
public final boolean setClassFilters(ClassFilter[] classFilters) {
|
||||
boolean changed = !filtersEqual(myClassFilters, classFilters);
|
||||
myClassFilters = classFilters;
|
||||
return changed;
|
||||
}
|
||||
|
||||
protected static boolean filtersEqual(Object[] a, Object[] b) {
|
||||
if ((a == null || a.length == 0) && (b == null || b.length == 0)) {
|
||||
return true;
|
||||
}
|
||||
return Comparing.equal(a, b);
|
||||
}
|
||||
|
||||
@XCollection(propertyElementName = "class-exclusion-filters")
|
||||
public ClassFilter[] getClassExclusionFilters() {
|
||||
return myClassExclusionFilters != null ? myClassExclusionFilters : ClassFilter.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
public boolean setClassExclusionFilters(ClassFilter[] classExclusionFilters) {
|
||||
boolean changed = !filtersEqual(myClassExclusionFilters, classExclusionFilters);
|
||||
myClassExclusionFilters = classExclusionFilters;
|
||||
return changed;
|
||||
}
|
||||
|
||||
public static TraceSettings getInstance() {
|
||||
return ServiceManager.getService(TraceSettings.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(@NotNull TraceSettings state) {
|
||||
myClassFilters = state.getClassFilters();
|
||||
myClassExclusionFilters = state.getClassExclusionFilters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceSettings getState() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.JDOMExternalizerUtil;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiCodeFragment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
@@ -282,6 +283,12 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
|
||||
|
||||
private void runAction(EvaluationContextImpl context, LocatableEvent event) {
|
||||
DebugProcessImpl debugProcess = context.getDebugProcess();
|
||||
if (getProperties().isTRACING_START() && Registry.is("debugger.call.tracing")) {
|
||||
CallTracer.get(debugProcess).start(context);
|
||||
}
|
||||
if (getProperties().isTRACING_END() && Registry.is("debugger.call.tracing")) {
|
||||
CallTracer.get(debugProcess).stop();
|
||||
}
|
||||
if (isLogEnabled() || isLogExpressionEnabled() || isLogStack()) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
if (myXBreakpoint.isLogMessage()) {
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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.DebugProcessEvents;
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
|
||||
import com.intellij.debugger.settings.TraceSettings;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.classFilter.ClassFilter;
|
||||
import com.sun.jdi.IncompatibleThreadStateException;
|
||||
import com.sun.jdi.event.Event;
|
||||
import com.sun.jdi.event.MethodEntryEvent;
|
||||
import com.sun.jdi.request.EventRequest;
|
||||
import com.sun.jdi.request.EventRequestManager;
|
||||
import com.sun.jdi.request.MethodEntryRequest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author egor
|
||||
*/
|
||||
public class CallTracer {
|
||||
private static final Logger LOG = Logger.getInstance(CallTracer.class);
|
||||
public static final Key<CallTracer> CALL_TRACER_KEY = Key.create("CALL_TRACER");
|
||||
|
||||
private final EventRequestManager myRequestManager;
|
||||
private final DebugProcessImpl myDebugProcess;
|
||||
private final List<MethodEntryRequest> myEntryRequests = new ArrayList<>(1);
|
||||
private int myStartIndent = 0;
|
||||
|
||||
public CallTracer(DebugProcessImpl debugProcess) {
|
||||
myDebugProcess = debugProcess;
|
||||
myRequestManager = debugProcess.getRequestsManager().getVMRequestManager();
|
||||
}
|
||||
|
||||
public void start(EvaluationContextImpl context) {
|
||||
if (myEntryRequests.isEmpty()) {
|
||||
myStartIndent = 0;
|
||||
try {
|
||||
ThreadReferenceProxyImpl thread = context.getSuspendContext().getThread();
|
||||
if (thread != null) {
|
||||
myStartIndent = thread.frameCount();
|
||||
}
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
TraceSettings traceSettings = TraceSettings.getInstance();
|
||||
ClassFilter[] classFilters = traceSettings.getClassFilters();
|
||||
ClassFilter[] exclusionFilters = traceSettings.getClassExclusionFilters();
|
||||
if (DebuggerUtilsEx.getEnabledNumber(classFilters) == 0) {
|
||||
addEntryRequest(null, exclusionFilters);
|
||||
}
|
||||
else {
|
||||
for (ClassFilter filter : classFilters) {
|
||||
if (filter.isEnabled()) {
|
||||
addEntryRequest(filter, exclusionFilters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addEntryRequest(ClassFilter filter, ClassFilter[] exclusionFilters) {
|
||||
MethodEntryRequest request = myRequestManager.createMethodEntryRequest();
|
||||
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); // to be able to detect frameCount inside the event handler
|
||||
myDebugProcess.getRequestsManager()
|
||||
.addClassFilters(request, filter != null ? new ClassFilter[]{filter} : ClassFilter.EMPTY_ARRAY, exclusionFilters);
|
||||
myEntryRequests.add(request);
|
||||
DebugProcessEvents.enableRequestWithHandler(request, this::accept);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (!myEntryRequests.isEmpty()) {
|
||||
myEntryRequests.forEach(myRequestManager::deleteEventRequest);
|
||||
myEntryRequests.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void accept(Event event) {
|
||||
if (event instanceof MethodEntryEvent) {
|
||||
MethodEntryEvent methodEntryEvent = (MethodEntryEvent)event;
|
||||
try {
|
||||
int indent = methodEntryEvent.thread().frameCount() - myStartIndent;
|
||||
if (indent < 0) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
myDebugProcess.printToConsole("\n" + StringUtil.repeat(" ", indent) + methodEntryEvent.method());
|
||||
}
|
||||
catch (IncompatibleThreadStateException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CallTracer get(DebugProcessImpl debugProcess) {
|
||||
CallTracer tracer = debugProcess.getUserData(CALL_TRACER_KEY);
|
||||
if (tracer == null) {
|
||||
tracer = new CallTracer(debugProcess);
|
||||
debugProcess.putUserData(CALL_TRACER_KEY, tracer);
|
||||
}
|
||||
return tracer;
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
// 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.settings.TraceSettings;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.ui.IdeBorderFactory;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
|
||||
import com.intellij.xdebugger.breakpoints.ui.XBreakpointCustomPropertiesPanel;
|
||||
import com.intellij.xdebugger.impl.breakpoints.XBreakpointBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class CallTracingPropertiesPanel extends XBreakpointCustomPropertiesPanel<XLineBreakpoint<JavaLineBreakpointProperties>> {
|
||||
private final Project myProject;
|
||||
private JBCheckBox myStartTracing;
|
||||
private JBCheckBox myEndTracing;
|
||||
|
||||
public CallTracingPropertiesPanel(Project project) {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
myStartTracing = new JBCheckBox("Start");
|
||||
myEndTracing = new JBCheckBox("Stop");
|
||||
JButton filters = new JButton("Filters...");
|
||||
//DialogUtil.registerMnemonic(myStartTracing);
|
||||
//DialogUtil.registerMnemonic(myEndTracing);
|
||||
|
||||
JPanel _panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 0));
|
||||
_panel.add(myStartTracing);
|
||||
_panel.add(myEndTracing);
|
||||
_panel.add(filters);
|
||||
_panel.setBorder(IdeBorderFactory.createTitledBorder("Tracing", true));
|
||||
|
||||
ActionListener listener = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JCheckBox toUncheck = null;
|
||||
if (myStartTracing.isSelected() && myEndTracing.isSelected()) {
|
||||
Object source = e.getSource();
|
||||
if (myStartTracing.equals(source)) {
|
||||
toUncheck = myEndTracing;
|
||||
}
|
||||
else if (myEndTracing.equals(source)) {
|
||||
toUncheck = myStartTracing;
|
||||
}
|
||||
if (toUncheck != null) {
|
||||
toUncheck.setSelected(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
myStartTracing.addActionListener(listener);
|
||||
myEndTracing.addActionListener(listener);
|
||||
|
||||
filters.addActionListener(e -> {
|
||||
EditClassFiltersDialog dialog = new EditClassFiltersDialog(myProject);
|
||||
TraceSettings traceSettings = TraceSettings.getInstance();
|
||||
dialog.setFilters(traceSettings.getClassFilters(), traceSettings.getClassExclusionFilters());
|
||||
dialog.setTitle("Tracing Class Filters");
|
||||
dialog.show();
|
||||
if (dialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
|
||||
traceSettings.setClassFilters(dialog.getFilters());
|
||||
traceSettings.setClassExclusionFilters(dialog.getExclusionFilters());
|
||||
}
|
||||
});
|
||||
|
||||
return _panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFrom(@NotNull XLineBreakpoint<JavaLineBreakpointProperties> breakpoint) {
|
||||
myStartTracing.setSelected(breakpoint.getProperties().isTRACING_START());
|
||||
myEndTracing.setSelected(breakpoint.getProperties().isTRACING_END());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveTo(@NotNull XLineBreakpoint<JavaLineBreakpointProperties> breakpoint) {
|
||||
boolean changed = breakpoint.getProperties().setTRACING_START(myStartTracing.isSelected());
|
||||
changed = breakpoint.getProperties().setTRACING_END(myEndTracing.isSelected()) || changed;
|
||||
if (changed) {
|
||||
((XBreakpointBase)breakpoint).fireBreakpointChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -10,6 +10,7 @@ import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
@@ -18,6 +19,7 @@ import com.intellij.xdebugger.XDebuggerUtil;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.intellij.xdebugger.breakpoints.XBreakpoint;
|
||||
import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
|
||||
import com.intellij.xdebugger.breakpoints.ui.XBreakpointCustomPropertiesPanel;
|
||||
import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
|
||||
import com.intellij.xdebugger.impl.XSourcePositionImpl;
|
||||
import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl;
|
||||
@@ -309,4 +311,13 @@ public class JavaLineBreakpointType extends JavaLineBreakpointTypeBase<JavaLineB
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public XBreakpointCustomPropertiesPanel<XLineBreakpoint<JavaLineBreakpointProperties>> createCustomPropertiesPanel(@NotNull Project project) {
|
||||
if (Registry.is("debugger.call.tracing")) {
|
||||
return new CallTracingPropertiesPanel(project);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+28
@@ -29,6 +29,9 @@ public class JavaBreakpointProperties<T extends JavaBreakpointProperties> extend
|
||||
private ClassFilter[] myCallerFilters;
|
||||
private ClassFilter[] myCallerExclusionFilters;
|
||||
|
||||
private boolean TRACING_START = false;
|
||||
private boolean TRACING_END = false;
|
||||
|
||||
@XCollection(propertyElementName = "instance-filters")
|
||||
public InstanceFilter[] getInstanceFilters() {
|
||||
return myInstanceFilters != null ? myInstanceFilters : InstanceFilter.EMPTY_ARRAY;
|
||||
@@ -100,6 +103,9 @@ public class JavaBreakpointProperties<T extends JavaBreakpointProperties> extend
|
||||
setCALLER_FILTERS_ENABLED(state.isCALLER_FILTERS_ENABLED());
|
||||
myCallerFilters = state.getCallerFilters();
|
||||
myCallerExclusionFilters = state.getCallerExclusionFilters();
|
||||
|
||||
setTRACING_START(state.isTRACING_START());
|
||||
setTRACING_END(state.isTRACING_END());
|
||||
}
|
||||
|
||||
@OptionTag("count-filter-enabled")
|
||||
@@ -178,4 +184,26 @@ public class JavaBreakpointProperties<T extends JavaBreakpointProperties> extend
|
||||
myCallerExclusionFilters = callerExclusionFilters;
|
||||
return changed;
|
||||
}
|
||||
|
||||
@OptionTag("tracing-start")
|
||||
public boolean isTRACING_START() {
|
||||
return TRACING_START;
|
||||
}
|
||||
|
||||
public boolean setTRACING_START(boolean TRACING_START) {
|
||||
boolean changed = this.TRACING_START != TRACING_START;
|
||||
this.TRACING_START = TRACING_START;
|
||||
return changed;
|
||||
}
|
||||
|
||||
@OptionTag("tracing-end")
|
||||
public boolean isTRACING_END() {
|
||||
return TRACING_END;
|
||||
}
|
||||
|
||||
public boolean setTRACING_END(boolean TRACING_END) {
|
||||
boolean changed = this.TRACING_END != TRACING_END;
|
||||
this.TRACING_END = TRACING_END;
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,6 +430,7 @@ debugger.attach.to.process.action=true
|
||||
debugger.sa.jdwp.debug=false
|
||||
debugger.evaluate.single.threaded.timeout=1000
|
||||
debugger.evaluate.single.threaded.timeout.description=Number of milliseconds to evaluate resuming only the current thread, then resume all threads
|
||||
debugger.call.tracing=false
|
||||
|
||||
execution.java.always.debug=false
|
||||
execution.java.always.debug.description=Always run java processes with the debug agent
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
<applicationService serviceImplementation="com.intellij.debugger.settings.DebuggerSettings"/>
|
||||
<projectService serviceImplementation="com.intellij.debugger.settings.DebuggerProjectSettings"/>
|
||||
<applicationService serviceImplementation="com.intellij.debugger.settings.ThreadsViewSettings"/>
|
||||
<applicationService serviceImplementation="com.intellij.debugger.settings.TraceSettings"/>
|
||||
|
||||
<applicationService serviceInterface="com.intellij.packaging.elements.PackagingElementFactory"
|
||||
serviceImplementation="com.intellij.packaging.impl.elements.PackagingElementFactoryImpl"/>
|
||||
|
||||
Reference in New Issue
Block a user