mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
1. added "copy to clipboard" action to thread dump console
2. thread dumps from a debug session are now shown in the relevant debug tab
This commit is contained in:
@@ -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<ThreadState> 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);
|
||||
}
|
||||
|
||||
@@ -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<ThreadState> 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);
|
||||
|
||||
@@ -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<ThreadState> 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<ThreadState> myThreadDump;
|
||||
private final Project myProject;
|
||||
|
||||
public CopyToClipboardAction(List<ThreadState> 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");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ThreadState> 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<ThreadState> 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";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user