/******************************************************************************* * Copyright (c) 2007, 2012 Wind River Systems, Inc. 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.internal.debug.ui.trace; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationListener; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.jface.resource.ColorRegistry; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabFolder2Adapter; import org.eclipse.swt.custom.CTabFolderEvent; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Text; import org.eclipse.tcf.core.AbstractChannel; import org.eclipse.tcf.internal.debug.model.TCFLaunch; import org.eclipse.tcf.protocol.IChannel; import org.eclipse.tcf.protocol.IPeer; import org.eclipse.tcf.protocol.JSON; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.util.TCFTask; import org.eclipse.ui.IWorkbenchPreferenceConstants; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.ViewPart; public class TraceView extends ViewPart implements Protocol.ChannelOpenListener { private Composite parent; private CTabFolder tabs; private Label no_data; private final Map<CTabItem,Page> tab2page = new HashMap<CTabItem,Page>(); private final ILaunchManager launch_manager = DebugPlugin.getDefault().getLaunchManager(); private final ILaunchConfigurationListener launch_conf_listener = new ILaunchConfigurationListener() { public void launchConfigurationAdded(ILaunchConfiguration cfg) { cfg = launch_manager.getMovedFrom(cfg); if (cfg != null) launchConfigurationChanged(cfg); } public void launchConfigurationChanged(final ILaunchConfiguration cfg) { HashSet<IChannel> set = new HashSet<IChannel>(); for (final ILaunch launch : launch_manager.getLaunches()) { if (launch instanceof TCFLaunch && cfg.equals(launch.getLaunchConfiguration())) { set.add(((TCFLaunch)launch).getChannel()); } } for (final IChannel channel : set) { parent.getDisplay().asyncExec(new Runnable() { public void run() { for (final Page p : tab2page.values()) { if (p.channel == channel) { p.tab.setToolTipText(new TCFTask<String>(p.channel) { public void run() { done(getPageToolTipText(p.channel)); } }.getE()); } } } }); } } public void launchConfigurationRemoved(ILaunchConfiguration cfg) { } }; private class Page implements AbstractChannel.TraceListener { final AbstractChannel channel; private CTabItem tab; private Text text; private final StringBuffer bf = new StringBuffer(); private int bf_line_cnt = 0; private boolean closed; private boolean scroll_locked; private int key_pressed; private int mouse_button_pressed; private final Thread update_thread = new Thread() { public void run() { synchronized (Page.this) { while (!closed) { if (bf_line_cnt > 0 && (!scroll_locked || bf_line_cnt >= 5000)) { 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); } }; parent.getDisplay().asyncExec(r); } try { Page.this.wait(1000); } catch (InterruptedException e) { break; } } } } }; Page(AbstractChannel channel) { this.channel = channel; update_thread.setName("TCF Trace View"); update_thread.start(); } private void updateScrollLock() { if (text == null) { scroll_locked = false; } else { scroll_locked = key_pressed > 0 || mouse_button_pressed > 0 || text.getSelectionCount() > 0; } } public void dispose() { if (closed) return; Protocol.invokeAndWait(new Runnable() { public void run() { channel.removeTraceListener(Page.this); } }); synchronized (this) { closed = true; update_thread.interrupt(); } try { update_thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } if (tab != null) { tab2page.remove(tab); tab.dispose(); tab = null; } text = null; if (tab2page.isEmpty()) hideTabs(); } public synchronized void onChannelClosed(Throwable error) { if (error == null) { parent.getDisplay().asyncExec(new Runnable() { public void run() { dispose(); } }); } else { bf.append("Channel terminated: " + error); bf_line_cnt++; } } public synchronized void onMessageReceived(char type, String token, String service, String name, byte[] data) { try { if ("Locator".equals(service) && "peerHeartBeat".equals(name)) return; appendTime(bf); bf.append("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) { appendData(bf, data); } 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) { try { if ("Locator".equals(service) && "peerHeartBeat".equals(name)) return; appendTime(bf); bf.append("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) { appendData(bf, data); } bf.append('\n'); bf_line_cnt++; } catch (UnsupportedEncodingException x) { x.printStackTrace(); } } } @Override public void createPartControl(Composite parent) { this.parent = parent; 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(); launch_manager.addLaunchConfigurationListener(launch_conf_listener); } @Override public void setFocus() { if (tabs != null) tabs.setFocus(); } @Override public void dispose() { launch_manager.removeLaunchConfigurationListener(launch_conf_listener); 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.dispose(); assert tab2page.isEmpty(); if (tabs != null) { tabs.dispose(); tabs = null; } if (no_data != null) { no_data.dispose(); no_data = null; } super.dispose(); } private String getPageTitle(IChannel c) { IPeer rp = c.getRemotePeer(); String title = rp.getName(); String host = rp.getAttributes().get(IPeer.ATTR_IP_HOST); String port = rp.getAttributes().get(IPeer.ATTR_IP_PORT); if (host != null) { title += ", " + host; if (port != null) { title += ":" + port; } } return title; } private String getPageToolTipText(IChannel c) { StringBuffer bf = new StringBuffer(); for (ILaunch launch : launch_manager.getLaunches()) { if (launch instanceof TCFLaunch && ((TCFLaunch)launch).getChannel() == c) { if (bf.length() > 0) bf.append('\n'); bf.append("Launch configuration: "); bf.append(launch.getLaunchConfiguration().getName()); } } IPeer rp = c.getRemotePeer(); String host = rp.getAttributes().get(IPeer.ATTR_IP_HOST); if (host != null) { if (bf.length() > 0) bf.append('\n'); bf.append("Agent address: "); bf.append(host); String port = rp.getAttributes().get(IPeer.ATTR_IP_PORT); if (port != null) { bf.append(':'); bf.append(port); } } if (bf.length() > 0) bf.append('\n'); bf.append("Agent name: "); bf.append(rp.getName()); String user_name = rp.getAttributes().get(IPeer.ATTR_USER_NAME); if (user_name != null) { bf.append('\n'); bf.append("Agent user: "); bf.append(user_name); } return bf.toString(); } public void onChannelOpen(final IChannel channel) { if (!(channel instanceof AbstractChannel)) return; AbstractChannel c = (AbstractChannel)channel; final Page p = new Page(c); c.addTraceListener(p); final String title = getPageTitle(c); final String tool_tip = getPageToolTipText(c); parent.getDisplay().asyncExec(new Runnable() { public void run() { if (parent.isDisposed()) return; showTabs(); p.tab = new CTabItem(tabs, SWT.NONE); tab2page.put(p.tab, p); p.tab.setText(title); p.tab.setToolTipText(tool_tip); p.text = new Text(tabs, SWT.H_SCROLL | SWT.V_SCROLL | SWT.READ_ONLY | SWT.MULTI); p.text.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE)); p.text.addKeyListener(new KeyListener() { public void keyReleased(KeyEvent e) { if (p.key_pressed > 0) p.key_pressed--; if (e.character == SWT.ESC) { p.key_pressed = 0; } p.updateScrollLock(); } public void keyPressed(KeyEvent e) { p.key_pressed++; p.updateScrollLock(); if (e.character == SWT.ESC) { p.text.clearSelection(); } } }); p.text.addMouseListener(new MouseListener() { public void mouseUp(MouseEvent e) { p.mouse_button_pressed--; p.updateScrollLock(); } public void mouseDown(MouseEvent e) { p.mouse_button_pressed++; p.updateScrollLock(); } public void mouseDoubleClick(MouseEvent e) { } }); p.tab.setControl(p.text); if (tabs.getSelection() == null) tabs.setSelection(p.tab); } }); } private void appendTime(StringBuffer bf) { String s = Long.toString(System.currentTimeMillis()); int l = s.length(); if (l < 6) return; bf.append(s.charAt(l - 6)); bf.append(s.charAt(l - 5)); bf.append(s.charAt(l - 4)); bf.append('.'); bf.append(s.charAt(l - 3)); bf.append(s.charAt(l - 2)); bf.append(s.charAt(l - 1)); bf.append(' '); } private void appendData(StringBuffer bf, byte[] data) throws UnsupportedEncodingException { int pos = bf.length(); try { Object[] o = JSON.parseSequence(data); for (int i = 0; i < o.length; i++) { bf.append(' '); appendJSON(bf, o[i]); } } catch (Throwable z) { bf.setLength(pos); for (int i = 0; i < data.length; i++) { bf.append(' '); int x = (data[i] >> 4) & 0xf; int y = data[i] & 0xf; bf.append((char)(x < 10 ? '0' + x : 'a' + x - 10)); bf.append((char)(y < 10 ? '0' + y : 'a' + y - 10)); } } } private void appendJSON(StringBuffer bf, Object o) { if (o instanceof byte[]) { int l = ((byte[])o).length; bf.append('('); bf.append(l); bf.append(')'); } else if (o instanceof Collection) { int cnt = 0; bf.append('['); for (Object i : (Collection<?>)o) { if (cnt > 0) bf.append(','); appendJSON(bf, i); cnt++; } bf.append(']'); } else if (o instanceof Map) { int cnt = 0; bf.append('{'); for (Object k : ((Map<?,?>)o).keySet()) { if (cnt > 0) bf.append(','); bf.append(k.toString()); bf.append(':'); appendJSON(bf, ((Map<?,?>)o).get(k)); cnt++; } bf.append('}'); } else if (o instanceof String) { bf.append('"'); String s = (String)o; int l = s.length(); for (int i = 0; i < l; i++) { char ch = s.charAt(i); if (ch < ' ') { bf.append('\\'); bf.append('u'); for (int j = 0; j < 4; j++) { int x = (ch >> (4 * (3 - j))) & 0xf; bf.append((char)(x < 10 ? '0' + x : 'a' + x - 10)); } } else { bf.append(ch); } } bf.append('"'); } else { bf.append(o); } } private void showTabs() { boolean b = false; if (no_data != null) { no_data.dispose(); no_data = null; b = true; } if (tabs == null && !parent.isDisposed()) { tabs = new CTabFolder(parent, SWT.FLAT | SWT.CLOSE); ColorRegistry reg = JFaceResources.getColorRegistry(); Color c1 = reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_START"); //$NON-NLS-1$ Color c2 = reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_BG_END"); //$NON-NLS-1$ tabs.setSelectionBackground(new Color[]{c1, c2}, new int[]{100}, true); tabs.setSelectionForeground(reg.get("org.eclipse.ui.workbench.ACTIVE_TAB_TEXT_COLOR")); //$NON-NLS-1$ tabs.setSimple(PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.SHOW_TRADITIONAL_STYLE_TABS)); tabs.addCTabFolder2Listener(new CTabFolder2Adapter() { public void close(CTabFolderEvent event) { CTabItem s = (CTabItem)event.item; Page p = tab2page.get(s); if (p != null) p.dispose(); else s.dispose(); event.doit = false; } }); Menu menu = new Menu(tabs); MenuItem mi_close = new MenuItem(menu, SWT.NONE); mi_close.setText("Close"); mi_close.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { if (tabs == null) return; CTabItem s = tabs.getSelection(); Page p = tab2page.get(s); if (p != null) p.dispose(); else s.dispose(); } }); MenuItem mi_close_all = new MenuItem(menu, SWT.NONE); mi_close_all.setText("Close All"); mi_close_all.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { if (tabs == null) return; CTabItem[] s = tabs.getItems(); for (CTabItem i : s) { Page p = tab2page.get(i); if (p != null) p.dispose(); else i.dispose(); } } }); tabs.setMenu(menu); b = true; } if (b) parent.layout(); } 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(); } } }