From 9363f8961bb866e5c1f0e49eb11bc180252a0dc4 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Mon, 21 Aug 2017 17:09:48 +0300 Subject: [PATCH] debugger overhead proto --- .../debugger/engine/JavaDebugProcess.java | 34 ++- .../intellij/debugger/ui/OverheadTimings.java | 48 ----- .../debugger/ui/breakpoints/Breakpoint.java | 2 +- .../debugger/ui/overhead/OverheadTimings.java | 84 ++++++++ .../debugger/ui/overhead/OverheadView.java | 198 ++++++++++++++++++ .../util/resources/misc/registry.properties | 1 + 6 files changed, 317 insertions(+), 50 deletions(-) delete mode 100644 java/debugger/impl/src/com/intellij/debugger/ui/OverheadTimings.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/ui/overhead/OverheadTimings.java create mode 100644 java/debugger/impl/src/com/intellij/debugger/ui/overhead/OverheadView.java diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java index 493e67f25771..ea854c894a0a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java @@ -35,6 +35,7 @@ import com.intellij.debugger.ui.impl.ThreadsPanel; import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; import com.intellij.debugger.ui.impl.watch.MessageDescriptor; import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; +import com.intellij.debugger.ui.overhead.OverheadView; import com.intellij.debugger.ui.tree.NodeDescriptor; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.ui.ExecutionConsole; @@ -318,6 +319,7 @@ public class JavaDebugProcess extends XDebugProcess { public void registerAdditionalContent(@NotNull RunnerLayoutUi ui) { registerThreadsPanel(ui); registerMemoryViewPanel(ui); + registerOverheadMonitor(ui); } @NotNull @@ -360,7 +362,8 @@ public class JavaDebugProcess extends XDebugProcess { } private void registerMemoryViewPanel(@NotNull RunnerLayoutUi ui) { - if (!Registry.get("debugger.enable.memory.view").asBoolean()) return; + if (!Registry.is("debugger.enable.memory.view")) return; + final XDebugSession session = getSession(); final DebugProcessImpl process = myJavaSession.getProcess(); final InstancesTracker tracker = InstancesTracker.getInstance(myJavaSession.getProject()); @@ -395,6 +398,35 @@ public class JavaDebugProcess extends XDebugProcess { } }, memoryViewContent); } + + private void registerOverheadMonitor(@NotNull RunnerLayoutUi ui) { + if (!Registry.is("debugger.enable.overhead.monitor")) return; + + OverheadView monitor = new OverheadView(myJavaSession.getProcess()); + Content overheadContent = ui.createContent("OverheadMonitor", monitor, "Overhead", AllIcons.Debugger.Db_obsolete, null); + + overheadContent.setCloseable(false); + overheadContent.setShouldDisposeContent(true); + + //session.addSessionListener(new XDebugSessionListener() { + // @Override + // public void sessionStopped() { + // session.removeSessionListener(this); + // data.getTrackedStacks().clear(); + // } + //}); + + ui.addContent(overheadContent, 0, PlaceInGrid.right, true); + //final DebuggerManagerThreadImpl managerThread = process.getManagerThread(); + //ui.addListener(new ContentManagerAdapter() { + // @Override + // public void selectionChanged(ContentManagerEvent event) { + // if (event != null && event.getContent() == overheadContent) { + // classesFilteredView.setActive(overheadContent.isSelected(), managerThread); + // } + // } + //}, overheadContent); + } }; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/OverheadTimings.java b/java/debugger/impl/src/com/intellij/debugger/ui/OverheadTimings.java deleted file mode 100644 index 501962e2cf1d..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/ui/OverheadTimings.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.debugger.ui; - -import com.intellij.debugger.engine.DebugProcessImpl; -import com.intellij.openapi.util.Key; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author egor - */ -public class OverheadTimings { - public static final Key KEY = Key.create("OVERHEAD_TIMINGS"); - - private final Map myMap = new ConcurrentHashMap<>(); - - public static float get(DebugProcessImpl process, Object producer) { - return getTimings(process).myMap.get(producer); - } - - public static void add(DebugProcessImpl process, Object producer, long overhead) { - getTimings(process).myMap.merge(producer, overhead, (old, value) -> old + value); - } - - private static OverheadTimings getTimings(DebugProcessImpl process) { - OverheadTimings data = process.getUserData(KEY); - if (data == null) { - data = new OverheadTimings(); - process.putUserData(KEY, data); - } - return data; - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java index 28ed78b216bc..7ee002caf789 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java @@ -33,7 +33,7 @@ import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.debugger.requests.ClassPrepareRequestor; import com.intellij.debugger.requests.Requestor; import com.intellij.debugger.settings.DebuggerSettings; -import com.intellij.debugger.ui.OverheadTimings; +import com.intellij.debugger.ui.overhead.OverheadTimings; import com.intellij.debugger.ui.impl.watch.CompilingEvaluatorImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/overhead/OverheadTimings.java b/java/debugger/impl/src/com/intellij/debugger/ui/overhead/OverheadTimings.java new file mode 100644 index 000000000000..aff39c23c71b --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/ui/overhead/OverheadTimings.java @@ -0,0 +1,84 @@ +/* + * 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.debugger.ui.overhead; + +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.openapi.util.Key; +import com.intellij.util.EventDispatcher; +import org.jetbrains.annotations.NotNull; + +import java.util.EventListener; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author egor + */ +public class OverheadTimings { + public static final Key KEY = Key.create("OVERHEAD_TIMINGS"); + + private final EventDispatcher myEventDispatcher = EventDispatcher.create(OverheadTimingsListener.class); + private final Map myMap = new ConcurrentHashMap<>(); + + public static long getTime(DebugProcessImpl process, Object producer) { + Timings timings = getTimings(process).myMap.get(producer); + return timings != null ? timings.myTime : 0; + } + + public static long getHits(DebugProcessImpl process, Object producer) { + Timings timings = getTimings(process).myMap.get(producer); + return timings != null ? timings.myHits : 0; + } + + public static Set getProducers(DebugProcessImpl process) { + return getTimings(process).myMap.keySet(); + } + + public static void add(DebugProcessImpl process, Object producer, long overhead) { + OverheadTimings timings = getTimings(process); + timings.myMap.merge(producer, new Timings(1, overhead), (old, value) -> new Timings(old.myHits + 1, old.myTime + overhead)); + timings.myEventDispatcher.getMulticaster().timingAdded(producer); + } + + @NotNull + private static OverheadTimings getTimings(DebugProcessImpl process) { + OverheadTimings data = process.getUserData(KEY); + if (data == null) { + data = new OverheadTimings(); + process.putUserData(KEY, data); + } + return data; + } + + private static class Timings { + final long myHits; + final long myTime; + + public Timings(long hits, long time) { + myHits = hits; + myTime = time; + } + } + + static void addListener(OverheadTimingsListener listener, DebugProcessImpl process) { + getTimings(process).myEventDispatcher.addListener(listener); + } + + public interface OverheadTimingsListener extends EventListener { + void timingAdded(Object producer); + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/overhead/OverheadView.java b/java/debugger/impl/src/com/intellij/debugger/ui/overhead/OverheadView.java new file mode 100644 index 000000000000..224dc36dbf9a --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/ui/overhead/OverheadView.java @@ -0,0 +1,198 @@ +/* + * 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.debugger.ui.overhead; + +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.ui.breakpoints.Breakpoint; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.TableUtil; +import com.intellij.ui.table.JBTable; +import com.intellij.util.ui.ColumnInfo; +import com.intellij.util.ui.ListTableModel; +import com.intellij.util.ui.components.BorderLayoutPanel; +import one.util.streamex.IntStreamEx; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.event.KeyEvent; +import java.util.Comparator; +import java.util.function.Function; + +/** + * @author egor + */ +public class OverheadView extends BorderLayoutPanel { + @NotNull private final DebugProcessImpl myProcess; + + static final EnabledColumnInfo ENABLED_COLUMN = new EnabledColumnInfo(); + static final NameColumnInfo NAME_COLUMN = new NameColumnInfo(); + + final JBTable myTable; + final ListTableModel myModel; + + public OverheadView(@NotNull DebugProcessImpl process) { + myProcess = process; + + myModel = new ListTableModel<>(new ColumnInfo[]{ + ENABLED_COLUMN, + NAME_COLUMN, + new TimingColumnInfo("hits", s -> OverheadTimings.getHits(myProcess, s.myBreakpoint)), + new TimingColumnInfo("time", s -> OverheadTimings.getTime(myProcess, s.myBreakpoint))}, + StreamEx.of(OverheadTimings.getProducers(process)).select(Breakpoint.class).map(BreakpointOverheadItem::new).toList(), + 3, SortOrder.DESCENDING); + myModel.setSortable(true); + myTable = new JBTable(myModel); + addToCenter(ScrollPaneFactory.createScrollPane(myTable)); + TableUtil.setupCheckboxColumn(myTable.getColumnModel().getColumn(0)); + OverheadTimings.addListener(o -> { + int idx = 0; + for (BreakpointOverheadItem item : myModel.getItems()) { + if (item.myBreakpoint == o) { + myModel.fireTableRowsUpdated(idx, idx); + return; + } + idx++; + } + myModel + .setItems(StreamEx.of(OverheadTimings.getProducers(process)).select(Breakpoint.class).map(BreakpointOverheadItem::new).toList()); + myModel.fireTableDataChanged(); + }, process); + + new DumbAwareAction("Toggle") { + @Override + public void update(@NotNull AnActionEvent e) { + e.getPresentation().setEnabled(myTable.getSelectedRowCount() == 1); + } + + @Override + public void actionPerformed(@NotNull final AnActionEvent e) { + getSelected().forEach(c -> c.setEnabled(!c.isEnabled())); + myTable.repaint(); + } + }.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0)), myTable); + } + + private StreamEx getSelected() { + return IntStreamEx.of(myTable.getSelectedRows()).map(myTable::convertRowIndexToModel).mapToObj(myModel::getItem); + } + + private static class EnabledColumnInfo extends ColumnInfo { + public EnabledColumnInfo() { + super(""); + } + + @Override + public Class getColumnClass() { + return Boolean.class; + } + + @Nullable + @Override + public Boolean valueOf(OverheadItem item) { + return item.isEnabled(); + } + + @Override + public boolean isCellEditable(OverheadItem item) { + return true; + } + + @Override + public void setValue(OverheadItem item, Boolean value) { + item.setEnabled(value); + } + } + + private static class NameColumnInfo extends ColumnInfo { + public NameColumnInfo() { + super("name"); + } + + @Nullable + @Override + public String valueOf(BreakpointOverheadItem aspects) { + return aspects.getName(); + } + + @Nullable + @Override + public Comparator getComparator() { + return Comparator.comparing(i -> valueOf(i)); + } + } + + private static class TimingColumnInfo extends ColumnInfo { + private final Function myGetter; + + public TimingColumnInfo(@NotNull String name, Function getter) { + super(name); + myGetter = getter; + } + + @Override + public Class getColumnClass() { + return Long.class; + } + + @Nullable + @Override + public Long valueOf(BreakpointOverheadItem aspects) { + return myGetter.apply(aspects); + } + + @Nullable + @Override + public Comparator getComparator() { + return Comparator.comparingLong(i -> valueOf(i)); + } + } + + interface OverheadItem { + boolean isEnabled(); + + void setEnabled(boolean enabled); + + String getName(); + } + + static class BreakpointOverheadItem implements OverheadItem { + private final Breakpoint myBreakpoint; + + public BreakpointOverheadItem(Breakpoint breakpoint) { + myBreakpoint = breakpoint; + } + + @Override + public String getName() { + return myBreakpoint.getDisplayName(); + } + + @Override + public boolean isEnabled() { + return myBreakpoint.isEnabled(); + } + + @Override + public void setEnabled(boolean enabled) { + myBreakpoint.setEnabled(enabled); + } + } +} diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 11dd59cef38a..49f455a4cae9 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -289,6 +289,7 @@ debugger.capture.points.annotations=false debugger.resume.yourkit.threads=false debugger.keep.step.requests=false debugger.enable.memory.view=true +debugger.enable.overhead.monitor=false debugger.tree.states.depth=100 analyze.exceptions.on.the.fly=false