unscramble -> java-impl

This commit is contained in:
Dmitry Jemerov
2009-09-01 20:00:56 +04:00
parent 60d40b9847
commit d2b2869be8
7 changed files with 0 additions and 0 deletions
@@ -0,0 +1,192 @@
package com.intellij.unscramble;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author yole
*/
public class ThreadDumpParser {
private static final Pattern ourThreadStartPattern = Pattern.compile("^\"(.+)\".+prio=\\d+ tid=[^\\s]+ nid=[^\\s]+ ([^\\[]+)");
private static final Pattern ourThreadStatePattern = Pattern.compile("java\\.lang\\.Thread\\.State: (.+) \\((.+)\\)");
private static final Pattern ourThreadStatePattern2 = Pattern.compile("java\\.lang\\.Thread\\.State: (.+)");
private static final Pattern ourWaitingForLockPattern = Pattern.compile("- waiting (on|to lock) <(.+)>");
private static final Pattern ourParkingToWaitForLockPattern = Pattern.compile("- parking to wait for <(.+)>");
@NonNls private static final String PUMP_EVENT = "java.awt.EventDispatchThread.pumpOneEventForFilters";
private static final Pattern ourIdleTimerThreadPattern = Pattern.compile("java.lang.Object.wait\\([^()]+\\)\\s+at java.util.TimerThread.mainLoop");
private static final Pattern ourIdleSwingTimerThreadPattern = Pattern.compile("java.lang.Object.wait\\([^()]+\\)\\s+at javax.swing.TimerQueue.run");
private ThreadDumpParser() {
}
public static List<ThreadState> parse(String threadDump) {
List<ThreadState> result = new ArrayList<ThreadState>();
StringBuilder lastThreadStack = new StringBuilder();
ThreadState lastThreadState = null;
boolean expectingThreadState = false;
boolean haveNonEmptyStackTrace = false;
for(@NonNls String line: StringUtil.tokenize(threadDump, "\r\n")) {
if (line.startsWith("============") || line.contains("Java-level deadlock")) {
break;
}
ThreadState state = tryParseThreadStart(line);
if (state != null) {
if (lastThreadState != null) {
lastThreadState.setStackTrace(lastThreadStack.toString(), !haveNonEmptyStackTrace);
}
lastThreadState = state;
result.add(lastThreadState);
lastThreadStack.setLength(0);
haveNonEmptyStackTrace = false;
lastThreadStack.append(line).append("\n");
expectingThreadState = true;
}
else {
boolean parsedThreadState = false;
if (expectingThreadState) {
expectingThreadState = false;
parsedThreadState = tryParseThreadState(line, lastThreadState);
}
lastThreadStack.append(line).append("\n");
if (!parsedThreadState && line.trim().startsWith("at")) {
haveNonEmptyStackTrace = true;
}
}
}
if (lastThreadState != null) {
lastThreadState.setStackTrace(lastThreadStack.toString(), !haveNonEmptyStackTrace);
}
for(ThreadState threadState: result) {
inferThreadStateDetail(threadState);
final String s = findWaitingForLock(threadState.getStackTrace());
if (s != null) {
for(ThreadState lockOwner : result) {
if (lockOwner == threadState) {
continue;
}
final String marker = "- locked <" + s + ">";
if (lockOwner.getStackTrace().contains(marker)) {
if (threadState.isAwaitedBy(lockOwner)) {
threadState.addDeadlockedThread(lockOwner);
lockOwner.addDeadlockedThread(threadState);
}
lockOwner.addWaitingThread(threadState);
break;
}
}
}
}
sortThreads(result);
return result;
}
public static void sortThreads(List<ThreadState> result) {
Collections.sort(result, new Comparator<ThreadState>() {
public int compare(final ThreadState o1, final ThreadState o2) {
return getInterestLevel(o2) - getInterestLevel(o1);
}
});
}
@Nullable
private static String findWaitingForLock(final String stackTrace) {
Matcher m = ourWaitingForLockPattern.matcher(stackTrace);
if (m.find()) {
return m.group(2);
}
m = ourParkingToWaitForLockPattern.matcher(stackTrace);
if (m.find()) {
return m.group(1);
}
return null;
}
private static int getInterestLevel(final ThreadState state) {
if (state.isEmptyStackTrace()) return -10;
if (isKnownJdkThread(state)) return -5;
if (state.isSleeping()) {
return -2;
}
if (state.getOperation() == ThreadOperation.Socket) {
return -1;
}
return state.getStackTrace().split("\n").length;
}
public static boolean isKnownJdkThread(final ThreadState state) {
@NonNls String stackTrace = state.getStackTrace();
return stackTrace.contains("java.lang.ref.Reference$ReferenceHandler.run") ||
stackTrace.contains("java.lang.ref.Finalizer$FinalizerThread.run") ||
stackTrace.contains("sun.awt.AWTAutoShutdown.run") ||
stackTrace.contains("sun.java2d.Disposer.run") ||
stackTrace.contains("sun.awt.windows.WToolkit.eventLoop") ||
ourIdleTimerThreadPattern.matcher(stackTrace).find() ||
ourIdleSwingTimerThreadPattern.matcher(stackTrace).find();
}
public static void inferThreadStateDetail(final ThreadState threadState) {
@NonNls String stackTrace = threadState.getStackTrace();
if (stackTrace.contains("at java.net.PlainSocketImpl.socketAccept") ||
stackTrace.contains("at java.net.PlainDatagramSocketImpl.receive") ||
stackTrace.contains("at java.net.SocketInputStream.socketRead") ||
stackTrace.contains("at java.net.PlainSocketImpl.socketConnect")) {
threadState.setOperation(ThreadOperation.Socket);
}
else if (stackTrace.contains("at java.io.FileInputStream.readBytes")) {
threadState.setOperation(ThreadOperation.IO);
}
else if (stackTrace.contains("at java.lang.Thread.sleep")) {
final String javaThreadState = threadState.getJavaThreadState();
if (!Thread.State.RUNNABLE.name().equals(javaThreadState)) {
threadState.setThreadStateDetail("sleeping"); // JDK 1.6 sets this explicitly, but JDK 1.5 does not
}
}
if (threadState.isEDT()) {
if (stackTrace.contains("java.awt.EventQueue.getNextEvent")) {
threadState.setThreadStateDetail("idle");
}
int modality = 0;
int pos = 0;
while(true) {
pos = stackTrace.indexOf(PUMP_EVENT, pos);
if (pos < 0) break;
modality++;
pos += PUMP_EVENT.length();
}
threadState.setExtraState("modality level " + modality);
}
}
@Nullable
private static ThreadState tryParseThreadStart(final String line) {
Matcher m = ourThreadStartPattern.matcher(line);
if (m.find()) {
return new ThreadState(m.group(1), m.group(2));
}
return null;
}
private static boolean tryParseThreadState(final String line, final ThreadState threadState) {
Matcher m = ourThreadStatePattern.matcher(line);
if (m.find()) {
threadState.setJavaThreadState(m.group(1));
threadState.setThreadStateDetail(m.group(2).trim());
return true;
}
m = ourThreadStatePattern2.matcher(line);
if (m.find()) {
threadState.setJavaThreadState(m.group(1));
return true;
}
return false;
}
}
@@ -0,0 +1,18 @@
package com.intellij.unscramble;
/**
* @author yole
*/
public enum ThreadOperation {
Socket("socket operation"), IO("I/O");
private final String myName;
ThreadOperation(final String name) {
myName = name;
}
public String toString() {
return myName;
}
}
@@ -0,0 +1,133 @@
package com.intellij.unscramble;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* @author yole
*/
public class ThreadState {
private final String myName;
private final String myState;
private String myStackTrace;
private boolean myEmptyStackTrace;
private String myJavaThreadState;
private String myThreadStateDetail;
private String myExtraState;
private final Set<ThreadState> myThreadsWaitingForMyLock = new HashSet<ThreadState>();
private final Set<ThreadState> myDeadlockedThreads = new HashSet<ThreadState>();
@Nullable
private ThreadOperation myOperation;
public ThreadState(final String name, final String state) {
myName = name;
myState = state.trim();
}
public String getName() {
return myName;
}
public String getState() {
return myState;
}
public String getStackTrace() {
return myStackTrace;
}
public void setStackTrace(final String stackTrace, boolean isEmpty) {
myStackTrace = stackTrace;
myEmptyStackTrace = isEmpty;
}
public Collection<ThreadState> getAwaitingThreads() {
return Collections.unmodifiableSet(myThreadsWaitingForMyLock);
}
public String toString() {
return myName;
}
public void setJavaThreadState(final String javaThreadState) {
myJavaThreadState = javaThreadState;
}
public void setThreadStateDetail(@NonNls final String threadStateDetail) {
myThreadStateDetail = threadStateDetail;
}
public String getJavaThreadState() {
return myJavaThreadState;
}
public String getThreadStateDetail() {
if (myOperation != null) {
return myOperation.toString();
}
return myThreadStateDetail;
}
public boolean isEmptyStackTrace() {
return myEmptyStackTrace;
}
public String getExtraState() {
return myExtraState;
}
public void setExtraState(final String extraState) {
myExtraState = extraState;
}
public boolean isSleeping() {
return "sleeping".equals(getThreadStateDetail()) ||
(("parking".equals(getThreadStateDetail()) || "waiting on condition".equals(myState)) && isThreadPoolExecutor());
}
private boolean isThreadPoolExecutor() {
return myStackTrace.contains("java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take") ||
myStackTrace.contains("java.util.concurrent.ThreadPoolExecutor.getTask");
}
public boolean isAwaitedBy(ThreadState thread) {
return myThreadsWaitingForMyLock.contains(thread);
}
public void addWaitingThread(ThreadState thread) {
myThreadsWaitingForMyLock.add(thread);
}
public boolean isDeadlocked() {
return !myDeadlockedThreads.isEmpty();
}
public void addDeadlockedThread(ThreadState thread) {
myDeadlockedThreads.add(thread);
}
@Nullable
public ThreadOperation getOperation() {
return myOperation;
}
public void setOperation(@Nullable final ThreadOperation operation) {
myOperation = operation;
}
public boolean isWaiting() {
return "on object monitor".equals(myThreadStateDetail) ||
myState.startsWith("waiting") ||
("parking".equals(myThreadStateDetail) && !isSleeping());
}
public boolean isEDT() {
return getName().startsWith("AWT-EventQueue");
}
}
@@ -0,0 +1,22 @@
package com.intellij.unscramble;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.DumbAware;
public final class UnscrambleAction extends AnAction implements DumbAware {
public void actionPerformed(AnActionEvent e) {
Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
UnscrambleDialog dialog = new UnscrambleDialog(project);
dialog.show();
}
public void update(AnActionEvent event){
Presentation presentation = event.getPresentation();
Project project = PlatformDataKeys.PROJECT.getData(event.getDataContext());
presentation.setEnabled(project != null);
}
}
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.unscramble.UnscrambleDialog">
<grid id="5ad95" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="82" y="81" width="244" height="151"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<xy id="a20bc" binding="myEditorPanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</xy>
<component id="458db" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="unscramble.stacktrace.caption"/>
</properties>
</component>
<grid id="26652" binding="myUnscramblePanel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="etched"/>
<children>
<component id="ef149" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="bafde"/>
<text resource-bundle="messages/IdeBundle" key="unscramble.unscrambler.combobox"/>
</properties>
</component>
<component id="bafde" class="javax.swing.JComboBox" binding="myUnscrambleChooser">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="64969" class="javax.swing.JLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="c5749"/>
<text resource-bundle="messages/IdeBundle" key="unscramble.log.path.label"/>
</properties>
</component>
<xy id="c5749" binding="myLogFileChooserPanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</xy>
<component id="8116e" class="javax.swing.JCheckBox" binding="myUseUnscrambler">
<constraints>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/IdeBundle" key="unscramble.use.unscrambler.checkbox"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
</form>
@@ -0,0 +1,450 @@
/**
* @author cdr
*/
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;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
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.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;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class UnscrambleDialog extends DialogWrapper{
@NonNls private static final String PROPERTY_LOG_FILE_HISTORY_URLS = "UNSCRAMBLE_LOG_FILE_URL";
@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;
private JComboBox myUnscrambleChooser;
private JPanel myPanel;
private TextFieldWithHistory myLogFile;
private JCheckBox myUseUnscrambler;
private JPanel myUnscramblePanel;
protected AnalyzeStacktraceUtil.StacktraceEditorPanel myStacktraceEditorPanel;
public UnscrambleDialog(Project project) {
super(false);
myProject = project;
populateRegisteredUnscramblerList();
myUnscrambleChooser.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
UnscrambleSupport unscrambleSupport = getSelectedUnscrambler();
GuiUtils.enableChildren(myLogFileChooserPanel, unscrambleSupport != null);
}
});
myUseUnscrambler.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
useUnscramblerChanged();
}
});
createLogFileChooser();
createEditor();
reset();
setTitle(IdeBundle.message("unscramble.dialog.title"));
init();
}
private void useUnscramblerChanged() {
boolean selected = myUseUnscrambler.isSelected();
GuiUtils.enableChildren(myUnscramblePanel, selected, myUseUnscrambler);
}
private void reset() {
final List<String> savedUrls = getSavedLogFileUrls();
myLogFile.setHistorySize(10);
myLogFile.setHistory(savedUrls);
String lastUrl = getLastUsedLogUrl();
if (lastUrl == null && !savedUrls.isEmpty()) {
lastUrl = savedUrls.get(savedUrls.size() - 1);
}
if (lastUrl != null) {
myLogFile.setText(lastUrl);
myLogFile.setSelectedItem(lastUrl);
}
final UnscrambleSupport selectedUnscrambler = getSavedUnscrambler();
final int count = myUnscrambleChooser.getItemCount();
int index = 0;
if (selectedUnscrambler != null) {
for (int i = 0; i < count; i++) {
final UnscrambleSupport unscrambleSupport = (UnscrambleSupport)myUnscrambleChooser.getItemAt(i);
if (unscrambleSupport != null && Comparing.strEqual(unscrambleSupport.getPresentableName(), selectedUnscrambler.getPresentableName())) {
index = i;
break;
}
}
}
if (count > 0) {
myUseUnscrambler.setEnabled(true);
myUnscrambleChooser.setSelectedIndex(index);
myUseUnscrambler.setSelected(selectedUnscrambler != null);
}
else {
myUseUnscrambler.setEnabled(false);
}
useUnscramblerChanged();
myStacktraceEditorPanel.pasteTextFromClipboard();
}
public static String getLastUsedLogUrl() {
return PropertiesComponent.getInstance().getValue(PROPERTY_LOG_FILE_LAST_URL);
}
@Nullable
public static UnscrambleSupport getSavedUnscrambler() {
final List<UnscrambleSupport> registeredUnscramblers = getRegisteredUnscramblers();
final String savedUnscramblerName = PropertiesComponent.getInstance().getValue(PROPERTY_UNSCRAMBLER_NAME_USED);
UnscrambleSupport selectedUnscrambler = null;
for (final UnscrambleSupport unscrambleSupport : registeredUnscramblers) {
if (Comparing.strEqual(unscrambleSupport.getPresentableName(), savedUnscramblerName)) {
selectedUnscrambler = unscrambleSupport;
}
}
return selectedUnscrambler;
}
public static List<String> getSavedLogFileUrls() {
final List<String> res = new ArrayList<String>();
final String savedUrl = PropertiesComponent.getInstance().getValue(PROPERTY_LOG_FILE_HISTORY_URLS);
final String[] strings = savedUrl == null ? ArrayUtil.EMPTY_STRING_ARRAY : savedUrl.split(":::");
for (int i = 0; i != strings.length; ++i) {
res.add(strings[i]);
}
return res;
}
@Nullable
private UnscrambleSupport getSelectedUnscrambler() {
if (!myUseUnscrambler.isSelected()) return null;
return (UnscrambleSupport)myUnscrambleChooser.getSelectedItem();
}
private void createEditor() {
myStacktraceEditorPanel = AnalyzeStacktraceUtil.createEditorPanel(myProject, myDisposable);
myEditorPanel.setLayout(new BorderLayout());
myEditorPanel.add(myStacktraceEditorPanel, BorderLayout.CENTER);
}
protected Action[] createActions(){
return new Action[]{new NormalizeTextAction(), getOKAction(), getCancelAction(), getHelpAction()};
}
private void createLogFileChooser() {
myLogFile = new TextFieldWithHistory();
JPanel panel = GuiUtils.constructFieldWithBrowseButton(myLogFile, new ActionListener() {
public void actionPerformed(ActionEvent e) {
FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor();
VirtualFile[] files = FileChooser.chooseFiles(myLogFile, descriptor);
if (files.length != 0) {
myLogFile.setText(FileUtil.toSystemDependentName(files[files.length-1].getPath()));
}
}
});
myLogFileChooserPanel.setLayout(new BorderLayout());
myLogFileChooserPanel.add(panel, BorderLayout.CENTER);
}
private void populateRegisteredUnscramblerList() {
List<UnscrambleSupport> unscrambleComponents = getRegisteredUnscramblers();
//myUnscrambleChooser.addItem(null);
for (final UnscrambleSupport unscrambleSupport : unscrambleComponents) {
myUnscrambleChooser.addItem(unscrambleSupport);
}
myUnscrambleChooser.setRenderer(new DefaultListCellRenderer() {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
UnscrambleSupport unscrambleSupport = (UnscrambleSupport)value;
setText(unscrambleSupport == null ? IdeBundle.message("unscramble.no.unscrambler.item") : unscrambleSupport.getPresentableName());
return this;
}
});
}
private static List<UnscrambleSupport> getRegisteredUnscramblers() {
final UnscrambleSupport[] components = Extensions.getExtensions(UnscrambleSupport.EP_NAME);
return Arrays.asList(components);
}
protected JComponent createCenterPanel() {
return myPanel;
}
public void dispose() {
if (isOK()){
final List list = myLogFile.getHistory();
String res = null;
for (Object aList : list) {
final String s = (String)aList;
if (res == null) {
res = s;
}
else {
res = res + ":::" + s;
}
}
PropertiesComponent.getInstance().setValue(PROPERTY_LOG_FILE_HISTORY_URLS, res);
UnscrambleSupport selectedUnscrambler = getSelectedUnscrambler();
PropertiesComponent.getInstance().setValue(PROPERTY_UNSCRAMBLER_NAME_USED, selectedUnscrambler == null ? null : selectedUnscrambler.getPresentableName());
PropertiesComponent.getInstance().setValue(PROPERTY_LOG_FILE_LAST_URL, myLogFile.getText());
}
super.dispose();
}
public void setText(String trace) {
myStacktraceEditorPanel.setText(trace);
}
private final class NormalizeTextAction extends AbstractAction {
public NormalizeTextAction(){
putValue(Action.NAME, IdeBundle.message("unscramble.normalize.button"));
putValue(DEFAULT_ACTION, Boolean.FALSE);
}
public void actionPerformed(ActionEvent e){
String text = myStacktraceEditorPanel.getText();
myStacktraceEditorPanel.setText(normalizeText(text));
}
}
static String normalizeText(@NonNls String text) {
StringBuilder builder = new StringBuilder(text.length());
String[] lines = text.split("\n");
boolean first = true;
boolean inAuxInfo = false;
for (String line : lines) {
//noinspection HardCodedStringLiteral
if (!inAuxInfo && (line.startsWith("JNI global references") || line.trim().equals("Heap"))) {
builder.append("\n");
inAuxInfo = true;
}
if (inAuxInfo) {
builder.append(trimSuffix(line)).append("\n");
continue;
}
if (!first && mustHaveNewLineBefore(line)) {
builder.append("\n");
if (line.startsWith("\"")) builder.append("\n"); // Additional linebreak for thread names
}
first = false;
int i = builder.lastIndexOf("\n");
CharSequence lastLine = i == -1 ? builder : builder.subSequence(i + 1, builder.length());
if (lastLine.toString().matches("\\s*at") && !line.matches("\\s+.*")) builder.append(" "); // separate 'at' from file name
builder.append(trimSuffix(line));
}
return builder.toString();
}
private static String trimSuffix(final String line) {
int len = line.length();
while ((0 < len) && (line.charAt(len-1) <= ' ')) {
len--;
}
return (len < line.length()) ? line.substring(0, len) : line;
}
private static boolean mustHaveNewLineBefore(String line) {
final int nonws = CharArrayUtil.shiftForward(line, 0, " \t");
if (nonws < line.length()) {
line = line.substring(nonws);
}
if (line.startsWith("at")) return true; // Start of the new stackframe entry
if (line.startsWith("Caused")) return true; // Caused by message
if (line.startsWith("- locked")) return true; // "Locked a monitor" logging
if (line.startsWith("- waiting")) return true; // "Waiting for monitor" logging
if (line.startsWith("- parking to wait")) return true;
if (line.startsWith("java.lang.Thread.State")) return true;
if (line.startsWith("\"")) return true; // Start of the new thread (thread name)
return false;
}
protected void doOKAction() {
if (performUnscramble()) {
myLogFile.addCurrentTextToHistory();
close(OK_EXIT_CODE);
}
}
public void doHelpAction() {
HelpManager.getInstance().invokeHelp("find.analyzeStackTrace");
}
private boolean performUnscramble() {
UnscrambleSupport selectedUnscrambler = getSelectedUnscrambler();
return showUnscrambledText(selectedUnscrambler, myLogFile.getText(), myProject, myStacktraceEditorPanel.getText());
}
static boolean showUnscrambledText(UnscrambleSupport unscrambleSupport, String logName, Project project, String textToUnscramble) {
String unscrambledTrace = unscrambleSupport == null ? textToUnscramble : unscrambleSupport.unscramble(project,textToUnscramble, logName);
if (unscrambledTrace == null) return false;
List<ThreadState> threadStates = ThreadDumpParser.parse(unscrambledTrace);
final ConsoleView consoleView = addConsole(project, threadStates);
AnalyzeStacktraceUtil.printStacktrace(consoleView, unscrambledTrace);
return true;
}
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);
}
} : 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";
}
public JComponent getPreferredFocusedComponent() {
return myStacktraceEditorPanel.getEditorComponent();
}
}
@@ -0,0 +1,40 @@
package com.intellij.unscramble;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
public final class UnscrambleFromClipboardAction extends AnAction {
public void actionPerformed(AnActionEvent e) {
Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
// If there's a text in clipboard and log is specified or not needed, unscramble w/o extra questions
String text = AnalyzeStacktraceUtil.getTextInClipboard();
if (text != null) {
String file = UnscrambleDialog.getLastUsedLogUrl();
if (file != null && file.trim().length() == 0) {
file = null;
}
UnscrambleSupport savedUnscrambler = UnscrambleDialog.getSavedUnscrambler();
if (savedUnscrambler == null || file != null) {
boolean success = UnscrambleDialog.showUnscrambledText(savedUnscrambler, file, project, text);
if (success) {
return;
}
}
}
// Use regular unscramble dialog
UnscrambleDialog dialog = new UnscrambleDialog(project);
dialog.show();
}
public void update(AnActionEvent event){
Presentation presentation = event.getPresentation();
Project project = PlatformDataKeys.PROJECT.getData(event.getDataContext());
presentation.setEnabled(project != null);
}
}