From 36f326590fc4edc0d8afc5c0471c78c2f0a4a019 Mon Sep 17 00:00:00 2001 From: irengrig Date: Sat, 13 Jul 2013 20:01:29 +0400 Subject: [PATCH] default java runner: thread dump action to show results in ThreadDumpPanel, with navigation to code and parsed into separate threads VERY much needed when running JUnit tests again and again and things are complicated, to check for deadlocks etc ! now is turned on only when -Didea.java.run.wise.thread.dump=true --- .../impl/DefaultJavaProgramRunner.java | 83 ++++++++++++++++++- .../unscramble/ThreadDumpConsoleFactory.java | 40 +++++++++ .../intellij/unscramble/UnscrambleDialog.java | 8 +- 3 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 java/java-impl/src/com/intellij/unscramble/ThreadDumpConsoleFactory.java diff --git a/java/execution/impl/src/com/intellij/execution/impl/DefaultJavaProgramRunner.java b/java/execution/impl/src/com/intellij/execution/impl/DefaultJavaProgramRunner.java index bfe8dd65e02f..0d9aa3f2e38b 100644 --- a/java/execution/impl/src/com/intellij/execution/impl/DefaultJavaProgramRunner.java +++ b/java/execution/impl/src/com/intellij/execution/impl/DefaultJavaProgramRunner.java @@ -21,6 +21,7 @@ import com.intellij.execution.ExecutionResult; import com.intellij.execution.Executor; import com.intellij.execution.configurations.*; import com.intellij.execution.executors.DefaultRunExecutor; +import com.intellij.execution.process.CapturingProcessAdapter; import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessHandler; @@ -28,23 +29,30 @@ import com.intellij.execution.runners.*; import com.intellij.execution.ui.ExecutionConsole; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.icons.AllIcons; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CustomShortcutSet; -import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; +import com.intellij.unscramble.AnalyzeStacktraceUtil; +import com.intellij.unscramble.ThreadDumpConsoleFactory; +import com.intellij.unscramble.ThreadDumpParser; +import com.intellij.unscramble.ThreadState; +import com.intellij.util.text.DateFormatUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; +import java.util.List; /** * @author spleaner */ public class DefaultJavaProgramRunner extends JavaPatchableProgramRunner { + private final static String ourWiseThreadDumpProperty = "idea.java.run.wise.thread.dump"; + @Override public boolean canRun(@NotNull final String executorId, @NotNull final RunProfile profile) { return executorId.equals(DefaultRunExecutor.EXECUTOR_ID) && @@ -166,11 +174,78 @@ public class DefaultJavaProgramRunner extends JavaPatchableProgramRunner { public void actionPerformed(final AnActionEvent e) { ProcessProxy proxy = ProcessProxyFactory.getInstance().getAttachedProxy(myProcessHandler); if (proxy != null) { + final WiseDumpThreadsListener wiseListener = Boolean.TRUE.equals(Boolean.getBoolean(ourWiseThreadDumpProperty)) ? + new WiseDumpThreadsListener(PlatformDataKeys.PROJECT.getData(e.getDataContext()), myProcessHandler) : null; + proxy.sendBreak(); + + if (wiseListener != null) { + wiseListener.after(); + } } } } + private static class WiseDumpThreadsListener { + private final Project myProject; + private final ProcessHandler myProcessHandler; + private final CapturingProcessAdapter myListener; + + public WiseDumpThreadsListener(final Project project, final ProcessHandler processHandler) { + myProject = project; + myProcessHandler = processHandler; + + myListener = new CapturingProcessAdapter(); + myProcessHandler.addProcessListener(myListener); + } + + public void after() { + if (myProject == null) { + myProcessHandler.removeProcessListener(myListener); + return; + } + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + if (myProcessHandler.isProcessTerminated() || myProcessHandler.isProcessTerminating()) return; + List threadStates = null; + final long start = System.currentTimeMillis(); + while ((System.currentTimeMillis() - start) < 1000) { + final String stdout = myListener.getOutput().getStdout(); + threadStates = ThreadDumpParser.parse(stdout); + if (threadStates == null || threadStates.isEmpty()) { + try { + Thread.sleep(50); + } + catch (InterruptedException e1) { + // + } + threadStates = null; + continue; + } + break; + } + myProcessHandler.removeProcessListener(myListener); + if (threadStates != null && ! threadStates.isEmpty()) { + showThreadDump(myListener.getOutput().getStdout(), threadStates); + } + } + + }); + } + + private void showThreadDump(final String out, final List threadStates) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + AnalyzeStacktraceUtil.addConsole(myProject, threadStates.size() > 1 ? + new ThreadDumpConsoleFactory(myProject, threadStates) : null, + " " + DateFormatUtil.formatDateTime(System.currentTimeMillis()), out); + } + }, ModalityState.NON_MODAL); + } + } + protected static class SoftExitAction extends LauncherBasedAction { public SoftExitAction(final ProcessHandler processHandler) { super(ExecutionBundle.message("run.configuration.exit.action.name"), null, AllIcons.Actions.Exit, processHandler); diff --git a/java/java-impl/src/com/intellij/unscramble/ThreadDumpConsoleFactory.java b/java/java-impl/src/com/intellij/unscramble/ThreadDumpConsoleFactory.java new file mode 100644 index 000000000000..94fa4f24575b --- /dev/null +++ b/java/java-impl/src/com/intellij/unscramble/ThreadDumpConsoleFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2013 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.unscramble; + +import com.intellij.execution.ui.ConsoleView; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.project.Project; + +import javax.swing.*; +import java.util.List; + +/** +* Created by Irina.Chernushina on 7/13/13. +*/ +public class ThreadDumpConsoleFactory implements AnalyzeStacktraceUtil.ConsoleFactory { + private final Project myProject; + private final List myThreadDump; + + public ThreadDumpConsoleFactory(Project project, List threadDump) { + myProject = project; + myThreadDump = threadDump; + } + + public JComponent createConsoleComponent(ConsoleView consoleView, DefaultActionGroup toolbarActions) { + return new ThreadDumpPanel(myProject, consoleView, toolbarActions, myThreadDump); + } +} diff --git a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java index 2627fd298b4e..37311251a2c0 100644 --- a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java +++ b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java @@ -15,12 +15,10 @@ */ package com.intellij.unscramble; -import com.intellij.execution.ui.ConsoleView; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.icons.AllIcons; import com.intellij.ide.IdeBundle; import com.intellij.ide.util.PropertiesComponent; -import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; @@ -410,11 +408,7 @@ public class UnscrambleDialog extends DialogWrapper { message = IdeBundle.message("unscramble.unscrambled.deadlock.tab"); icon = AllIcons.Debugger.KillProcess; } - return AnalyzeStacktraceUtil.addConsole(project, threadDump.size() > 1 ? new AnalyzeStacktraceUtil.ConsoleFactory() { - public JComponent createConsoleComponent(ConsoleView consoleView, DefaultActionGroup toolbarActions) { - return new ThreadDumpPanel(project, consoleView, toolbarActions, threadDump); - } - } : null, message, unscrambledTrace, icon); + return AnalyzeStacktraceUtil.addConsole(project, threadDump.size() > 1 ? new ThreadDumpConsoleFactory(project, threadDump) : null, message, unscrambledTrace, icon); } protected String getDimensionServiceKey(){