diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ThreadDumpAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ThreadDumpAction.java index f4d986042438..67b08af6eb56 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/ThreadDumpAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/ThreadDumpAction.java @@ -11,6 +11,8 @@ import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.impl.DebuggerSession; import com.intellij.debugger.jdi.VirtualMachineProxyImpl; +import com.intellij.debugger.ui.DebuggerPanelsManager; +import com.intellij.debugger.ui.DebuggerSessionTab; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; @@ -20,7 +22,6 @@ import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.project.Project; import com.intellij.unscramble.ThreadDumpParser; import com.intellij.unscramble.ThreadState; -import com.intellij.unscramble.UnscrambleDialog; import com.sun.jdi.*; import java.util.ArrayList; @@ -47,7 +48,10 @@ public class ThreadDumpAction extends AnAction { final List threads = buildThreadStates(vm); ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { - UnscrambleDialog.addConsole(project, threads); + final DebuggerSessionTab sessionTab = DebuggerPanelsManager.getInstance(project).getSessionTab(); + if (sessionTab != null) { + sessionTab.addThreadDump(threads); + } } }, ModalityState.NON_MODAL); } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java index 2bd44f2c954d..ce1bbcab6011 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java @@ -18,6 +18,7 @@ import com.intellij.debugger.ui.impl.watch.*; import com.intellij.execution.*; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.executors.DefaultDebugExecutor; +import com.intellij.execution.filters.TextConsoleBuilderFactory; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.runners.RestartAction; @@ -41,6 +42,8 @@ import com.intellij.ui.content.AlertIcon; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManagerAdapter; import com.intellij.ui.content.ContentManagerEvent; +import com.intellij.unscramble.ThreadDumpPanel; +import com.intellij.unscramble.ThreadState; import com.intellij.xdebugger.XDebuggerBundle; import com.intellij.xdebugger.impl.actions.XDebuggerActions; import com.intellij.xdebugger.impl.ui.DebuggerLogConsoleManagerBase; @@ -50,6 +53,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.TreePath; import java.util.Collection; +import java.util.List; public class DebuggerSessionTab extends DebuggerLogConsoleManagerBase implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.DebuggerSessionTab"); @@ -75,6 +79,7 @@ public class DebuggerSessionTab extends DebuggerLogConsoleManagerBase implements public static final String BREAKPOINT_CONDITION = "breakpoint"; private final ThreadsPanel myThreadsPanel; + private static final String THREAD_DUMP_CONTENT_PREFIX = "Dump"; public DebuggerSessionTab(Project project, String sessionName) { super(project); @@ -481,6 +486,49 @@ public class DebuggerSessionTab extends DebuggerLogConsoleManagerBase implements myUi.selectAndFocus(myUi.findContent(DebuggerContentInfo.FRAME_CONTENT), false); } + private int myThreadDumpsCount = 0; + private int myCurrentThreadDumpId = 1; + + public void addThreadDump(List threads) { + final Project project = getProject(); + final ConsoleView consoleView = TextConsoleBuilderFactory.getInstance().createBuilder(project).getConsole(); + final DefaultActionGroup toolbarActions = new DefaultActionGroup(); + final ThreadDumpPanel panel = new ThreadDumpPanel(project, consoleView, toolbarActions, threads); + + final Icon icon = null; + final String id = createThreadDumpContentId(); + final Content content = myUi.createContent(id, panel, id, icon, null); + content.setCloseable(true); + content.setDescription("Thread Dump"); + myUi.addListener(new ContentManagerAdapter() { + public void contentRemoved(ContentManagerEvent event) { + if (event.getContent() == content) { + myThreadDumpsCount -= 1; + if (myThreadDumpsCount == 0) { + myCurrentThreadDumpId = 1; + } + myUi.removeListener(this); + } + } + }, content); + myUi.addContent(content); + myThreadDumpsCount += 1; + myCurrentThreadDumpId += 1; + Disposer.register(this, new Disposable() { + public void dispose() { + myUi.removeContent(content, true); + } + }); + myUi.selectAndFocus(content, false); + if (threads.size() > 0) { + panel.selectStackFrame(0); + } + } + + private String createThreadDumpContentId() { + return THREAD_DUMP_CONTENT_PREFIX + " #" + myCurrentThreadDumpId; + } + private class MyDebuggerStateManager extends DebuggerStateManager { public void fireStateChanged(DebuggerContextImpl newContext, int event) { super.fireStateChanged(newContext, event); diff --git a/java/java-impl/src/com/intellij/unscramble/ThreadDumpPanel.java b/java/java-impl/src/com/intellij/unscramble/ThreadDumpPanel.java new file mode 100644 index 000000000000..619489f94477 --- /dev/null +++ b/java/java-impl/src/com/intellij/unscramble/ThreadDumpPanel.java @@ -0,0 +1,164 @@ +package com.intellij.unscramble; + +import com.intellij.execution.ui.ConsoleView; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.ide.CopyPasteManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.Splitter; +import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.wm.ToolWindowId; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.LightColors; +import com.intellij.ui.ListToolTipHandler; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.util.ui.UIUtil; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.*; +import java.awt.datatransfer.StringSelection; +import java.util.List; + +public class ThreadDumpPanel extends JPanel { + private static final Icon PAUSE_ICON = IconLoader.getIcon("/debugger/threadStates/paused.png"); + private static final Icon LOCKED_ICON = IconLoader.getIcon("/debugger/threadStates/locked.png"); + private static final Icon RUNNING_ICON = IconLoader.getIcon("/debugger/threadStates/running.png"); + private static final Icon SOCKET_ICON = IconLoader.getIcon("/debugger/threadStates/socket.png"); + private static final Icon IDLE_ICON = IconLoader.getIcon("/debugger/threadStates/idle.png"); + private static final Icon EDT_BUSY_ICON = IconLoader.getIcon("/debugger/threadStates/edtBusy.png"); + private static final Icon IO_ICON = IconLoader.getIcon("/debugger/threadStates/io.png"); + private JList myThreadList; + + public ThreadDumpPanel(Project project, final ConsoleView consoleView, final DefaultActionGroup toolbarActions, final List threadDump) { + super(new BorderLayout()); + + myThreadList = new JList(threadDump.toArray(new ThreadState[threadDump.size()])); + myThreadList.setCellRenderer(new ThreadListCellRenderer()); + myThreadList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + myThreadList.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(final ListSelectionEvent e) { + int index = myThreadList.getSelectedIndex(); + if (index >= 0) { + ThreadState selection = threadDump.get(index); + AnalyzeStacktraceUtil.printStacktrace(consoleView, selection.getStackTrace()); + } + else { + AnalyzeStacktraceUtil.printStacktrace(consoleView, ""); + } + myThreadList.repaint(); + } + }); + ListToolTipHandler.install(myThreadList); + toolbarActions.add(new CopyToClipboardAction(threadDump, project)); + add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarActions,false).getComponent(), BorderLayout.WEST); + + final Splitter splitter = new Splitter(false, 0.3f); + splitter.setFirstComponent(new JScrollPane(myThreadList)); + splitter.setSecondComponent(consoleView.getComponent()); + + add(splitter, BorderLayout.CENTER); + } + + private static Icon getThreadStateIcon(final ThreadState threadState) { + if (threadState.isSleeping()) { + return PAUSE_ICON; + } + if (threadState.isWaiting()) { + return LOCKED_ICON; + } + if (threadState.getOperation() == ThreadOperation.Socket) { + return SOCKET_ICON; + } + if (threadState.getOperation() == ThreadOperation.IO) { + return IO_ICON; + } + if (threadState.isEDT()) { + if ("idle".equals(threadState.getThreadStateDetail())) { + return IDLE_ICON; + } + return EDT_BUSY_ICON; + } + return RUNNING_ICON; + } + + private static SimpleTextAttributes getAttributes(final ThreadState threadState) { + if (threadState.isSleeping()) { + return SimpleTextAttributes.GRAY_ATTRIBUTES; + } + if (threadState.isEmptyStackTrace() || ThreadDumpParser.isKnownJdkThread(threadState)) { + return new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, Color.GRAY.brighter()); + } + if (threadState.isEDT()) { + return SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES; + } + return SimpleTextAttributes.REGULAR_ATTRIBUTES; + } + + private static class ThreadListCellRenderer extends ColoredListCellRenderer { + + protected void customizeCellRenderer(final JList list, final Object value, final int index, final boolean selected, final boolean hasFocus) { + ThreadState threadState = (ThreadState) value; + setIcon(getThreadStateIcon(threadState)); + if (!selected) { + ThreadState selectedThread = (ThreadState)list.getSelectedValue(); + if (threadState.isDeadlocked()) { + setBackground(LightColors.RED); + } + else if (selectedThread != null && threadState.isAwaitedBy(selectedThread)) { + setBackground(Color.YELLOW); + } + else { + setBackground(UIUtil.getListBackground()); + } + } + SimpleTextAttributes attrs = getAttributes(threadState); + append(threadState.getName() + " (", attrs); + String detail = threadState.getThreadStateDetail(); + if (detail == null) { + detail = threadState.getState(); + } + if (detail.length() > 30) { + detail = detail.substring(0, 30) + "..."; + } + append(detail, attrs); + append(")", attrs); + if (threadState.getExtraState() != null) { + append(" [" + threadState.getExtraState() + "]", attrs); + } + } + } + + public void selectStackFrame(int index) { + myThreadList.setSelectedIndex(index); + } + + + private static class CopyToClipboardAction extends AnAction { + private final List myThreadDump; + private final Project myProject; + + public CopyToClipboardAction(List threadDump, Project project) { + super("Copy to Clipboard", "Copy whole thread dump to clipboard", IconLoader.getIcon("/general/copy.png")); + myThreadDump = threadDump; + myProject = project; + } + + public void actionPerformed(AnActionEvent e) { + final StringBuilder buf = new StringBuilder(); + for (ThreadState state : myThreadDump) { + buf.append(state.getStackTrace()).append("\n\n"); + } + CopyPasteManager.getInstance().setContents(new StringSelection(buf.toString())); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (!myProject.isDisposed()) { + ToolWindowManager.getInstance(myProject).notifyByBalloon(ToolWindowId.RUN, MessageType.INFO, "Full thread dump was successfully copied to clipboard"); + } + } + }); + } + } +} diff --git a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java index 77ce465642aa..297ffd12876f 100644 --- a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java +++ b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java @@ -6,9 +6,6 @@ package com.intellij.unscramble; import com.intellij.execution.ui.ConsoleView; import com.intellij.ide.IdeBundle; import com.intellij.ide.util.PropertiesComponent; -import com.intellij.openapi.actionSystem.ActionGroup; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileChooser.FileChooser; @@ -17,21 +14,17 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.*; +import com.intellij.ui.GuiUtils; +import com.intellij.ui.TextFieldWithHistory; import com.intellij.util.ArrayUtil; import com.intellij.util.text.CharArrayUtil; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -44,14 +37,6 @@ public class UnscrambleDialog extends DialogWrapper{ @NonNls private static final String PROPERTY_LOG_FILE_LAST_URL = "UNSCRAMBLE_LOG_FILE_LAST_URL"; @NonNls private static final String PROPERTY_UNSCRAMBLER_NAME_USED = "UNSCRAMBLER_NAME_USED"; - private static final Icon PAUSE_ICON = IconLoader.getIcon("/debugger/threadStates/paused.png"); - private static final Icon LOCKED_ICON = IconLoader.getIcon("/debugger/threadStates/locked.png"); - private static final Icon RUNNING_ICON = IconLoader.getIcon("/debugger/threadStates/running.png"); - private static final Icon SOCKET_ICON = IconLoader.getIcon("/debugger/threadStates/socket.png"); - private static final Icon IDLE_ICON = IconLoader.getIcon("/debugger/threadStates/idle.png"); - private static final Icon EDT_BUSY_ICON = IconLoader.getIcon("/debugger/threadStates/edtBusy.png"); - private static final Icon IO_ICON = IconLoader.getIcon("/debugger/threadStates/io.png"); - private final Project myProject; private JPanel myEditorPanel; private JPanel myLogFileChooserPanel; @@ -335,111 +320,11 @@ public class UnscrambleDialog extends DialogWrapper{ public static ConsoleView addConsole(final Project project, final List threadDump) { return AnalyzeStacktraceUtil.addConsole(project, threadDump.size() > 1 ? new AnalyzeStacktraceUtil.ConsoleFactory() { public JComponent createConsoleComponent(ConsoleView consoleView, DefaultActionGroup toolbarActions) { - return new ThreadDumpPanel(consoleView, toolbarActions, threadDump); + return new ThreadDumpPanel(project, consoleView, toolbarActions, threadDump); } } : null, IdeBundle.message("unscramble.unscrambled.stacktrace.tab")); } - private static class ThreadDumpPanel extends JPanel { - public ThreadDumpPanel(final ConsoleView consoleView, final ActionGroup toolbarActions, final List threadDump) { - super(new BorderLayout()); - - final JList threadList = new JList(threadDump.toArray(new ThreadState[threadDump.size()])); - threadList.setCellRenderer(new ThreadListCellRenderer()); - threadList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - threadList.addListSelectionListener(new ListSelectionListener() { - public void valueChanged(final ListSelectionEvent e) { - int index = threadList.getSelectedIndex(); - if (index >= 0) { - ThreadState selection = threadDump.get(index); - AnalyzeStacktraceUtil.printStacktrace(consoleView, selection.getStackTrace()); - } - else { - AnalyzeStacktraceUtil.printStacktrace(consoleView, ""); - } - threadList.repaint(); - } - }); - ListToolTipHandler.install(threadList); - add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarActions,false).getComponent(), BorderLayout.WEST); - - final Splitter splitter = new Splitter(false, 0.3f); - splitter.setFirstComponent(new JScrollPane(threadList)); - splitter.setSecondComponent(consoleView.getComponent()); - - add(splitter, BorderLayout.CENTER); - } - - private static Icon getThreadStateIcon(final ThreadState threadState) { - if (threadState.isSleeping()) { - return PAUSE_ICON; - } - if (threadState.isWaiting()) { - return LOCKED_ICON; - } - if (threadState.getOperation() == ThreadOperation.Socket) { - return SOCKET_ICON; - } - if (threadState.getOperation() == ThreadOperation.IO) { - return IO_ICON; - } - if (threadState.isEDT()) { - if ("idle".equals(threadState.getThreadStateDetail())) { - return IDLE_ICON; - } - return EDT_BUSY_ICON; - } - return RUNNING_ICON; - } - - private static SimpleTextAttributes getAttributes(final ThreadState threadState) { - if (threadState.isSleeping()) { - return SimpleTextAttributes.GRAY_ATTRIBUTES; - } - if (threadState.isEmptyStackTrace() || ThreadDumpParser.isKnownJdkThread(threadState)) { - return new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, Color.GRAY.brighter()); - } - if (threadState.isEDT()) { - return SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES; - } - return SimpleTextAttributes.REGULAR_ATTRIBUTES; - } - - private static class ThreadListCellRenderer extends ColoredListCellRenderer { - - protected void customizeCellRenderer(final JList list, final Object value, final int index, final boolean selected, final boolean hasFocus) { - ThreadState threadState = (ThreadState) value; - setIcon(getThreadStateIcon(threadState)); - if (!selected) { - ThreadState selectedThread = (ThreadState)list.getSelectedValue(); - if (threadState.isDeadlocked()) { - setBackground(LightColors.RED); - } - else if (selectedThread != null && threadState.isAwaitedBy(selectedThread)) { - setBackground(Color.YELLOW); - } - else { - setBackground(UIUtil.getListBackground()); - } - } - SimpleTextAttributes attrs = getAttributes(threadState); - append(threadState.getName() + " (", attrs); - String detail = threadState.getThreadStateDetail(); - if (detail == null) { - detail = threadState.getState(); - } - if (detail.length() > 30) { - detail = detail.substring(0, 30) + "..."; - } - append(detail, attrs); - append(")", attrs); - if (threadState.getExtraState() != null) { - append(" [" + threadState.getExtraState() + "]", attrs); - } - } - } - } - protected String getDimensionServiceKey(){ return "#com.intellij.unscramble.UnscrambleDialog"; } diff --git a/platform/lang-impl/src/com/intellij/unscramble/AnalyzeStacktraceUtil.java b/platform/lang-impl/src/com/intellij/unscramble/AnalyzeStacktraceUtil.java index 338f5f8ef89e..4a60d0d545f4 100644 --- a/platform/lang-impl/src/com/intellij/unscramble/AnalyzeStacktraceUtil.java +++ b/platform/lang-impl/src/com/intellij/unscramble/AnalyzeStacktraceUtil.java @@ -61,7 +61,7 @@ public class AnalyzeStacktraceUtil { return text; } - interface ConsoleFactory { + public interface ConsoleFactory { JComponent createConsoleComponent(ConsoleView consoleView, DefaultActionGroup toolbarActions); }