blob: d64ce8c98fc9d750104c1d740dd3cd8bd9e42e07 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 Nokia and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nokia - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.edc.internal.ui;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.tm.tcf.core.AbstractChannel;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IPeer;
import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;
public class TraceView extends ViewPart implements Protocol.ChannelOpenListener {
private static final String FILTER_HEARTBEATS = "filter_heartbeats"; //$NON-NLS-1$
private static final String REUSE_TABS = "reuse_tabs"; //$NON-NLS-1$
private Composite parent;
private TabFolder tabs;
private Label no_data;
private final Map<TabItem, Page> tab2page = new HashMap<TabItem, Page>();
private Action clearTabAction;
private Action closeTabAction;
private Action exportAction;
private Action filterAction;
private Action reuseAction;
private IMemento memento;
private class Page implements AbstractChannel.TraceListener {
final AbstractChannel channel;
private TabItem tab;
private Text text;
private final StringBuffer bf = new StringBuffer();
private int bf_line_cnt = 0;
private boolean closed;
private final Thread update_thread = new Thread() {
@Override
public void run() {
synchronized (Page.this) {
while (!closed) {
if (bf_line_cnt > 0) {
Runnable r = new Runnable() {
public void run() {
String str = null;
int cnt = 0;
synchronized (Page.this) {
str = bf.toString();
cnt = bf_line_cnt;
bf.setLength(0);
bf_line_cnt = 0;
}
if (text == null)
return;
if (text.getLineCount() > 1000 - cnt) {
String s = text.getText();
int n = 0;
int i = -1;
while (n < cnt) {
int j = s.indexOf('\n', i + 1);
if (j < 0)
break;
i = j;
n++;
}
if (i >= 0) {
text.setText(s.substring(i + 1));
}
}
text.append(str);
}
};
getSite().getShell().getDisplay().asyncExec(r);
}
try {
Page.this.wait(1000);
} catch (InterruptedException e) {
break;
}
}
}
}
};
Page(AbstractChannel channel) {
this.channel = channel;
update_thread.start();
}
public void dispose() {
synchronized (this) {
closed = true;
update_thread.interrupt();
}
try {
update_thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
tab2page.remove(tab);
tab.dispose();
tab = null;
text = null;
if (tab2page.isEmpty())
hideTabs();
}
public synchronized void onChannelClosed(Throwable error) {
String msg = "";
if (error == null) {
msg = "Channel closed";
} else {
msg = "Channel terminated: " + error;
}
bf.append("\n<================= " + msg + " =================>\n");
bf_line_cnt += 3;
getSite().getShell().getDisplay().asyncExec(new Runnable() {
public void run() {
updateCloseAction();
}
});
}
public synchronized void onMessageReceived(char type, String token, String service, String name, byte[] data) {
if (memento.getBoolean(FILTER_HEARTBEATS)) {
if (name != null && name.contains("HeartBeat"))
return;
}
try {
bf.append("Time(ms): " + System.currentTimeMillis() + " Inp: " );
bf.append(type);
if (token != null) {
bf.append(' ');
bf.append(token);
}
if (service != null) {
bf.append(' ');
bf.append(service);
}
if (name != null) {
bf.append(' ');
bf.append(name);
}
if (data != null) {
int i = 0;
while (i < data.length) {
int j = i;
while (j < data.length && data[j] != 0)
j++;
bf.append(' ');
bf.append(new String(data, i, j - i, "UTF8"));
if (j < data.length && data[j] == 0)
j++;
i = j;
}
}
bf.append('\n');
bf_line_cnt++;
} catch (UnsupportedEncodingException x) {
x.printStackTrace();
}
}
public synchronized void onMessageSent(char type, String token, String service, String name, byte[] data) {
if (memento.getBoolean(FILTER_HEARTBEATS)) {
if (name != null && name.contains("HeartBeat"))
return;
}
try {
bf.append("Time(ms): " + System.currentTimeMillis() + " Out: ");
bf.append(type);
if (token != null) {
bf.append(' ');
bf.append(token);
}
if (service != null) {
bf.append(' ');
bf.append(service);
}
if (name != null) {
bf.append(' ');
bf.append(name);
}
if (data != null) {
int i = 0;
while (i < data.length) {
int j = i;
while (j < data.length && data[j] != 0)
j++;
bf.append(' ');
bf.append(new String(data, i, j - i, "UTF8"));
if (j < data.length && data[j] == 0)
j++;
i = j;
}
}
bf.append('\n');
bf_line_cnt++;
} catch (UnsupportedEncodingException x) {
x.printStackTrace();
}
}
}
@Override
public void init(IViewSite site, IMemento memento) throws PartInitException {
super.init(site, memento);
if (memento == null)
this.memento = XMLMemento.createWriteRoot("EDCTRACEVIEW"); //$NON-NLS-1$
else
this.memento = memento;
Preferences p = getViewPreferences(); // never returns null
this.memento.putBoolean(FILTER_HEARTBEATS, p.getBoolean(FILTER_HEARTBEATS, true));
this.memento.putBoolean(REUSE_TABS, p.getBoolean(REUSE_TABS, true));
}
@Override
public void saveState(IMemento memento) {
if (this.memento == null || memento == null)
return;
this.memento.putString(FILTER_HEARTBEATS, filterAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
this.memento.putString(REUSE_TABS, reuseAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
memento.putMemento(this.memento);
saveViewPreferences();
}
private void saveViewPreferences() {
Preferences preferences = getViewPreferences();
preferences.putBoolean(FILTER_HEARTBEATS, this.memento.getBoolean(FILTER_HEARTBEATS).booleanValue());
preferences.putBoolean(REUSE_TABS, this.memento.getBoolean(REUSE_TABS).booleanValue());
try {
preferences.flush();
} catch (BackingStoreException e) {
// empty
}
}
private Preferences getViewPreferences() {
return EDCDebugger.getPrefs(EDCDebugUI.PLUGIN_ID);
}
@Override
public void createPartControl(Composite parent) {
this.parent = parent;
createActions();
Protocol.invokeAndWait(new Runnable() {
public void run() {
IChannel[] arr = Protocol.getOpenChannels();
for (IChannel c : arr)
onChannelOpen(c);
Protocol.addChannelOpenListener(TraceView.this);
}
});
if (tab2page.size() == 0)
hideTabs();
}
private void createActions() {
IToolBarManager toolbarManager = getViewSite().getActionBars().getToolBarManager();
clearTabAction = new Action(null) {
public void run() {
Page currentPage = getCurrentPage();
if (currentPage != null) {
currentPage.text.setText(""); //$NON-NLS-1$
}
}
};
clearTabAction.setToolTipText("Clear the current tab");
clearTabAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EDCDebugUI.PLUGIN_ID,
"/icons/etool16/clear_tab.gif")); //$NON-NLS-1$
clearTabAction.setDisabledImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EDCDebugUI.PLUGIN_ID,
"/icons/dtool16/clear_tab.gif")); //$NON-NLS-1$
toolbarManager.add(clearTabAction);
closeTabAction = new Action(null) {
public void run() {
Page currentPage = getCurrentPage();
if (currentPage != null) {
currentPage.dispose();
}
}
};
closeTabAction.setToolTipText("Close the current tab");
closeTabAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EDCDebugUI.PLUGIN_ID,
"/icons/etool16/close_tab.gif")); //$NON-NLS-1$
closeTabAction.setDisabledImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EDCDebugUI.PLUGIN_ID,
"/icons/dtool16/close_tab.gif")); //$NON-NLS-1$
toolbarManager.add(closeTabAction);
exportAction = new Action(null) {
public void run() {
Page currentPage = getCurrentPage();
if (currentPage != null) {
handleExport(currentPage);
}
}
};
exportAction.setToolTipText("Export Log");
exportAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EDCDebugUI.PLUGIN_ID,
"/icons/etool16/export_log.gif")); //$NON-NLS-1$
exportAction.setDisabledImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(EDCDebugUI.PLUGIN_ID,
"/icons/dtool16/export_log.gif")); //$NON-NLS-1$
toolbarManager.add(exportAction);
IMenuManager mgr = getViewSite().getActionBars().getMenuManager();
filterAction = new Action("Filter heartbeats") { //$NON-NLS-1$
public void run() {
memento.putString(FILTER_HEARTBEATS, isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
}
};
filterAction.setChecked(memento.getString(FILTER_HEARTBEATS).equals("true")); //$NON-NLS-1$
mgr.add(filterAction);
reuseAction = new Action("Reuse tab for channel") { //$NON-NLS-1$
public void run() {
memento.putString(REUSE_TABS, isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
}
};
reuseAction.setChecked(memento.getString(REUSE_TABS).equals("true")); //$NON-NLS-1$
mgr.add(reuseAction);
}
private Page getCurrentPage() {
if (tabs != null) {
int index = tabs.getSelectionIndex();
if (index >= 0) {
return tab2page.get(tabs.getSelection()[0]);
}
}
return null;
}
@Override
public void setFocus() {
if (tabs != null)
tabs.setFocus();
}
@Override
public void dispose() {
saveViewPreferences();
final Page[] pages = tab2page.values().toArray(new Page[tab2page.size()]);
Protocol.invokeAndWait(new Runnable() {
public void run() {
Protocol.removeChannelOpenListener(TraceView.this);
for (Page p : pages)
p.channel.removeTraceListener(p);
}
});
for (Page p : pages)
p.dispose();
assert tab2page.isEmpty();
if (tabs != null) {
tabs.dispose();
tabs = null;
}
if (no_data != null) {
no_data.dispose();
no_data = null;
}
super.dispose();
}
public void onChannelOpen(final IChannel channel) {
if (!(channel instanceof AbstractChannel))
return;
AbstractChannel c = (AbstractChannel) channel;
if (memento.getBoolean(REUSE_TABS)) {
// see if the same channel is being opened again. if so just clear
// and reuse it.
for (final Page page : tab2page.values()) {
if (page.channel.getRemotePeer().equals(channel.getRemotePeer())) {
c.addTraceListener(page);
getSite().getShell().getDisplay().asyncExec(new Runnable() {
public void run() {
showTabs();
// select the new tab automatically
tabs.setSelection(page.tab);
updateCloseAction();
}
});
return;
}
}
}
IPeer rp = c.getRemotePeer();
final String name = rp.getName();
final String host = rp.getAttributes().get(IPeer.ATTR_IP_HOST);
final String port = rp.getAttributes().get(IPeer.ATTR_IP_PORT);
final Page p = new Page(c);
c.addTraceListener(p);
getSite().getShell().getDisplay().asyncExec(new Runnable() {
public void run() {
showTabs();
p.tab = new TabItem(tabs, SWT.NONE);
tab2page.put(p.tab, p);
String title = name;
if (host != null) {
title += ", " + host;
if (port != null) {
title += ":" + port;
}
}
p.tab.setText(title);
p.text = new Text(tabs, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.MULTI);
p.tab.setControl(p.text);
p.text.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
// select the new tab automatically
tabs.setSelection(p.tab);
updateCloseAction();
}
});
}
private void showTabs() {
boolean b = false;
if (no_data != null) {
no_data.dispose();
no_data = null;
b = true;
}
if (tabs == null) {
tabs = new TabFolder(parent, SWT.NONE);
tabs.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
updateCloseAction();
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
b = true;
}
if (b)
parent.layout();
clearTabAction.setEnabled(true);
closeTabAction.setEnabled(true);
exportAction.setEnabled(true);
}
private void updateCloseAction() {
// disable the close action if the current tab
// has an active channel so people don't accidentally
// close it and have no way to get it back.
Page page = getCurrentPage();
if (page != null) {
closeTabAction.setEnabled(page.channel.getState() == IChannel.STATE_CLOSED);
}
}
private void hideTabs() {
boolean b = false;
if (tabs != null) {
tabs.dispose();
tabs = null;
b = true;
}
if (!parent.isDisposed()) {
if (no_data == null) {
no_data = new Label(parent, SWT.NONE);
no_data.setText("No open communication channels at this time.");
b = true;
}
if (b)
parent.layout();
}
clearTabAction.setEnabled(false);
closeTabAction.setEnabled(false);
exportAction.setEnabled(false);
}
private void handleExport(Page currentPage) {
FileDialog dialog = new FileDialog(getViewSite().getShell(), SWT.SAVE);
dialog.setFilterExtensions(new String[] {"*.log"}); //$NON-NLS-1$
String path = dialog.open();
if (path != null) {
if (path.indexOf('.') == -1 && !path.endsWith(".log")) //$NON-NLS-1$
path += ".log"; //$NON-NLS-1$
File outputFile = new Path(path).toFile();
if (outputFile.exists()) {
String message = "File exists. Would you like to overwrite it?";
if (!MessageDialog.openQuestion(getViewSite().getShell(), "Export Log", message))
return;
}
Writer out = null;
try {
out = new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8"); //$NON-NLS-1$
} catch (IOException ex) {
return;
}
Reader in = new StringReader(currentPage.text.getText());
copy(in, out);
try {
if (in != null)
in.close();
} catch (IOException e1) { // do nothing
}
try {
if (out != null)
out.close();
} catch (IOException e1) { // do nothing
}
}
}
private void copy(Reader input, Writer output) {
String line;
BufferedReader reader = new BufferedReader(input);
BufferedWriter writer = new BufferedWriter(output);
try {
while (reader.ready() && ((line = reader.readLine()) != null)) {
writer.write(line);
writer.newLine();
}
} catch (IOException e) { // do nothing
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e1) { // do nothing
}
try
{
if (writer != null)
writer.close();
} catch (IOException e1) { // do nothing
}
}
}
}