mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
unscramble -> java-impl
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user