blob: e970653db12aa910fef5d9c24c3645cfd2d5c746 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* created at Dec 14, 2001
* @author Jeka
*/
package com.intellij.debugger.ui;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.DebuggerInvocationUtil;
import com.intellij.debugger.HelpID;
import com.intellij.debugger.actions.ThreadDumpAction;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.intellij.debugger.ui.impl.watch.MessageDescriptor;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.help.HelpManager;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.ScrollPaneFactory;
import com.sun.jdi.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.List;
public class ExportDialog extends DialogWrapper {
private final JTextArea myTextArea = new JTextArea();
private TextFieldWithBrowseButton myTfFilePath;
private final Project myProject;
private final DebugProcessImpl myDebugProcess;
private final CopyToClipboardAction myCopyToClipboardAction = new CopyToClipboardAction();
private static final @NonNls String DEFAULT_REPORT_FILE_NAME = "threads_report.txt";
public ExportDialog(DebugProcessImpl debugProcess, String destinationDirectory) {
super(debugProcess.getProject(), true);
myDebugProcess = debugProcess;
myProject = debugProcess.getProject();
setTitle(DebuggerBundle.message("threads.export.dialog.title"));
setOKButtonText(DebuggerBundle.message("button.save"));
init();
setOKActionEnabled(false);
myCopyToClipboardAction.setEnabled(false);
myTextArea.setText(MessageDescriptor.EVALUATING.getLabel());
debugProcess.getManagerThread().invoke(new ExportThreadsCommand(ApplicationManager.getApplication().getModalityStateForComponent(myTextArea)));
myTfFilePath.setText(destinationDirectory + File.separator + DEFAULT_REPORT_FILE_NAME);
setHorizontalStretch(1.5f);
}
@NotNull
protected Action[] createActions(){
return new Action[]{getOKAction(), myCopyToClipboardAction, getCancelAction(), getHelpAction()};
}
protected void doHelpAction() {
HelpManager.getInstance().invokeHelp(HelpID.EXPORT_THREADS);
}
protected JComponent createNorthPanel() {
JPanel box = new JPanel(new BorderLayout());
box.add(new JLabel(DebuggerBundle.message("label.threads.export.dialog.file")), BorderLayout.WEST);
myTfFilePath = new TextFieldWithBrowseButton();
myTfFilePath.addBrowseFolderListener(null, null, myProject, FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor());
box.add(myTfFilePath, BorderLayout.CENTER);
JPanel panel = new JPanel(new BorderLayout());
panel.add(box, BorderLayout.CENTER);
panel.add(Box.createVerticalStrut(7), BorderLayout.SOUTH);
return panel;
}
protected JComponent createCenterPanel() {
myTextArea.setEditable(false);
JScrollPane pane = ScrollPaneFactory.createScrollPane(myTextArea);
pane.setPreferredSize(new Dimension(400, 300));
return pane;
}
protected void doOKAction() {
String path = myTfFilePath.getText();
File file = new File(path);
if (file.isDirectory()) {
Messages.showMessageDialog(
myProject,
DebuggerBundle.message("error.threads.export.dialog.file.is.directory"),
DebuggerBundle.message("threads.export.dialog.title"),
Messages.getErrorIcon()
);
}
else if (file.exists()) {
int answer = Messages.showYesNoDialog(
myProject,
DebuggerBundle.message("error.threads.export.dialog.file.already.exists", path),
DebuggerBundle.message("threads.export.dialog.title"),
Messages.getQuestionIcon()
);
if (answer == Messages.YES) {
super.doOKAction();
}
}
else {
super.doOKAction();
}
}
public String getFilePath() {
return myTfFilePath.getText();
}
public String getTextToSave() {
return myTextArea.getText();
}
protected String getDimensionServiceKey(){
return "#com.intellij.debugger.ui.ExportDialog";
}
public static String getExportThreadsText(VirtualMachineProxyImpl vmProxy) {
final StringBuffer buffer = new StringBuffer(512);
List<ThreadReference> threads = vmProxy.getVirtualMachine().allThreads();
for (ThreadReference threadReference : threads) {
final String name = threadName(threadReference);
if (name == null) {
continue;
}
buffer.append(name);
ReferenceType referenceType = threadReference.referenceType();
if (referenceType != null) {
//noinspection HardCodedStringLiteral
Field daemon = referenceType.fieldByName("daemon");
if (daemon != null) {
Value value = threadReference.getValue(daemon);
if (value instanceof BooleanValue && ((BooleanValue)value).booleanValue()) {
buffer.append(" ").append(DebuggerBundle.message("threads.export.attribute.label.daemon"));
}
}
//noinspection HardCodedStringLiteral
Field priority = referenceType.fieldByName("priority");
if (priority != null) {
Value value = threadReference.getValue(priority);
if (value instanceof IntegerValue) {
buffer.append(", ").append(DebuggerBundle.message("threads.export.attribute.label.priority", ((IntegerValue)value).intValue()));
}
}
}
ThreadGroupReference groupReference = threadReference.threadGroup();
if (groupReference != null) {
buffer.append(", ").append(DebuggerBundle.message("threads.export.attribute.label.group", groupReference.name()));
}
buffer.append(", ").append(
DebuggerBundle.message("threads.export.attribute.label.status", DebuggerUtilsEx.getThreadStatusText(threadReference.status())));
try {
if (vmProxy.canGetOwnedMonitorInfo() && vmProxy.canGetMonitorInfo()) {
List<ObjectReference> list = threadReference.ownedMonitors();
for (ObjectReference reference : list) {
final List<ThreadReference> waiting = reference.waitingThreads();
for (ThreadReference thread : waiting) {
final String waitingThreadName = threadName(thread);
if (waitingThreadName != null) {
buffer.append("\n\t ").append(DebuggerBundle.message("threads.export.attribute.label.blocks.thread", waitingThreadName));
}
}
}
}
ObjectReference waitedMonitor = vmProxy.canGetCurrentContendedMonitor() ? threadReference.currentContendedMonitor() : null;
if (waitedMonitor != null) {
if (vmProxy.canGetMonitorInfo()) {
ThreadReference waitedThread = waitedMonitor.owningThread();
if (waitedThread != null) {
final String waitedThreadName = threadName(waitedThread);
if (waitedThreadName != null) {
buffer.append("\n\t ").append(DebuggerBundle.message("threads.export.attribute.label.waiting.for.thread", waitedThreadName,
ThreadDumpAction.renderObject(waitedMonitor)));
}
}
}
}
final List<StackFrame> frames = threadReference.frames();
for (StackFrame stackFrame : frames) {
final Location location = stackFrame.location();
buffer.append("\n\t ").append(renderLocation(location));
}
}
catch (IncompatibleThreadStateException e) {
buffer.append("\n\t ").append(DebuggerBundle.message("threads.export.attribute.error.incompatible.state"));
}
buffer.append("\n\n");
}
return buffer.toString();
}
private static String renderLocation(final Location location) {
String sourceName;
try {
sourceName = location.sourceName();
}
catch (AbsentInformationException e) {
sourceName = "Unknown Source";
}
return DebuggerBundle.message(
"export.threads.stackframe.format",
location.declaringType().name() + "." + location.method().name(),
sourceName,
location.lineNumber()
);
}
private static String threadName(ThreadReference threadReference) {
try {
return threadReference.name() + "@" + threadReference.uniqueID();
}
catch (ObjectCollectedException e) {
return null;
}
}
private class CopyToClipboardAction extends AbstractAction {
public CopyToClipboardAction() {
super(DebuggerBundle.message("button.copy"));
putValue(Action.SHORT_DESCRIPTION, DebuggerBundle.message("export.dialog.copy.action.description"));
}
public void actionPerformed(ActionEvent e) {
String s = StringUtil.convertLineSeparators(myTextArea.getText());
CopyPasteManager.getInstance().setContents(new StringSelection(s));
}
}
private class ExportThreadsCommand extends DebuggerCommandImpl {
protected ModalityState myModalityState;
public ExportThreadsCommand(ModalityState modalityState) {
myModalityState = modalityState;
}
private void setText(final String text) {
DebuggerInvocationUtil.invokeLater(myProject, new Runnable() {
public void run() {
myTextArea.setText(text);
setOKActionEnabled(true);
myCopyToClipboardAction.setEnabled(true);
}
}, myModalityState);
}
protected void action() {
setText(getExportThreadsText(myDebugProcess.getVirtualMachineProxy()));
}
}
}