/******************************************************************************* * Copyright (c) 2012-2015 INRIA. * 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: * Generoso Pagano - initial API and implementation ******************************************************************************/ /** * */ package fr.inria.soctrace.framesoc.ui.eventtable.view; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.resource.FontDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.TableEditor; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.themes.ColorUtil; import org.eclipse.wb.swt.ResourceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import fr.inria.linuxtools.tmf.ui.widgets.virtualtable.ColumnData; import fr.inria.linuxtools.tmf.ui.widgets.virtualtable.TmfVirtualTable; import fr.inria.soctrace.framesoc.core.bus.FramesocBus; import fr.inria.soctrace.framesoc.core.bus.FramesocBusTopic; import fr.inria.soctrace.framesoc.ui.eventtable.Activator; import fr.inria.soctrace.framesoc.ui.eventtable.loader.EventLoader; import fr.inria.soctrace.framesoc.ui.eventtable.loader.IEventLoader; import fr.inria.soctrace.framesoc.ui.eventtable.model.EventTableColumn; import fr.inria.soctrace.framesoc.ui.eventtable.model.EventTableRow; import fr.inria.soctrace.framesoc.ui.eventtable.model.EventTableRowFilter; import fr.inria.soctrace.framesoc.ui.model.EventTableDescriptor; import fr.inria.soctrace.framesoc.ui.model.GanttTraceIntervalAction; import fr.inria.soctrace.framesoc.ui.model.HistogramTraceIntervalAction; import fr.inria.soctrace.framesoc.ui.model.LoaderQueue; import fr.inria.soctrace.framesoc.ui.model.PieTraceIntervalAction; import fr.inria.soctrace.framesoc.ui.model.SynchronizeTraceIntervalAction; import fr.inria.soctrace.framesoc.ui.model.TimeInterval; import fr.inria.soctrace.framesoc.ui.model.TraceConfigurationDescriptor; import fr.inria.soctrace.framesoc.ui.model.TraceIntervalDescriptor; import fr.inria.soctrace.framesoc.ui.perspective.FramesocPart; import fr.inria.soctrace.framesoc.ui.perspective.FramesocPartManager; import fr.inria.soctrace.framesoc.ui.perspective.FramesocViews; import fr.inria.soctrace.framesoc.ui.utils.TimeBar; import fr.inria.soctrace.lib.model.Event; import fr.inria.soctrace.lib.model.EventProducer; import fr.inria.soctrace.lib.model.Trace; import fr.inria.soctrace.lib.model.utils.ModelConstants.EventCategory; import fr.inria.soctrace.lib.model.utils.ModelConstants.TimeUnit; import fr.inria.soctrace.lib.model.utils.SoCTraceException; import fr.inria.soctrace.lib.query.EventProducerQuery; import fr.inria.soctrace.lib.storage.TraceDBObject; import fr.inria.soctrace.lib.utils.DeltaManager; /** * Class for the Event table view * * @author "Generoso Pagano <generoso.pagano@inria.fr>" */ public final class EventTableView extends FramesocPart { /** * Logger */ private final static Logger logger = LoggerFactory.getLogger(EventTableView.class); /** * The ID of the view as specified by the extension. */ public static final String ID = FramesocViews.EVENT_TABLE_VIEW_ID; /** * Hint for filter row */ private static final String FILTER_HINT = "<filter>"; /** * Cache of event table rows */ private EventTableCache cache = new EventTableCache(); /** * Virtual table */ private TmfVirtualTable table; /** * Status text */ private Text statusText; /** * Time bar */ private TimeBar timeBar; /** * Start timestamp currently loaded */ private long startTimestamp; /** * End timestamp currently loaded */ private long endTimestamp; /** * Flag for enabling/disabling the filter */ private boolean filterEnabled = false; // SWT resources private LocalResourceManager resourceManager = new LocalResourceManager( JFaceResources.getResources()); private Color grayColor; private Color blackColor; private Font boldFont; // Filtering private int filterMatchCount; private int filterCheckCount; private FilterThread filterThread; private EventTableRowFilter rowFilter = new EventTableRowFilter(); private final Object filterSyncObj = new Object(); // Loading private Job loaderJob; private DrawerThread drawerThread; // Export private CSVExport csvExport; private final Object exportSyncObj = new Object(); private Map<EventTableColumn, Boolean> columnSelection; private String exportFileName; // Focus on one event private boolean focusOnEvent = false; private EventTableDescriptor focusEventDescriptor = null; /** * Keys for table data */ public interface Key { /** Column object, set on a column */ String COLUMN_OBJ = "$field_id"; //$NON-NLS-1$ /** Filter text, set on a column */ String FILTER_TXT = "$fltr_txt"; //$NON-NLS-1$ /** Filter object, set on the table */ String FILTER_OBJ = "$fltr_obj"; //$NON-NLS-1$ } /* * GUI creation */ @Override public void createFramesocPartControl(Composite parent) { // parent layout GridLayout gl_parent = new GridLayout(1, false); gl_parent.verticalSpacing = 2; gl_parent.marginWidth = 0; gl_parent.horizontalSpacing = 0; gl_parent.marginHeight = 0; parent.setLayout(gl_parent); // table layout Composite tableComposite = new Composite(parent, SWT.FILL); tableComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); GridLayout gl_table = new GridLayout(1, false); gl_table.verticalSpacing = 0; gl_table.marginWidth = 0; gl_table.horizontalSpacing = 0; gl_table.marginHeight = 0; tableComposite.setLayout(gl_table); // -------- // TABLE // -------- // Create the virtual table final int style = SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER; table = new TmfVirtualTable(tableComposite, style); // Set the table layout final GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); table.setLayoutData(layoutData); // Create resources createResources(); // Some cosmetic enhancements table.setHeaderVisible(true); table.setLinesVisible(true); // Set the columns setColumnHeaders(); // Set the frozen row for header row table.setFrozenRowCount(1); // Create the header row cell editor createHeaderEditor(); // Handle the table item requests table.addListener(SWT.SetData, new Listener() { @Override public void handleEvent(final org.eclipse.swt.widgets.Event event) { final TableItem item = (TableItem) event.item; int index = event.index - 1; // -1 for the header row if (event.index == 0) { setHeaderRowItemData(item); return; } final EventTableRow row = cache.get(index); if (row != null) { item.setText(getItemStrings(row)); return; } // Else, fill the cache asynchronously (and off the UI thread) event.doit = false; } }); // Listener for double click table.addListener(SWT.MouseDoubleClick, new Listener() { @Override public void handleEvent(org.eclipse.swt.widgets.Event event) { // Do not process if empty or multiple selection if(table.getSelection().length != 1) return; int index = table.getSelectionIndex() - 1; // -1 for the header row // If header row, don't process if(index < 0) return; switchToGantt(cache.get(index)); } }); table.setItemCount(1); // only the header at the beginning // ---------- // TOOLBAR // ---------- IToolBarManager manager = getViewSite().getActionBars().getToolBarManager(); manager.add(createColumnAction()); manager.add(createCSVExportAction()); manager.add(new Separator()); GanttTraceIntervalAction.add(manager, createGanttAction()); PieTraceIntervalAction.add(manager, createPieAction()); HistogramTraceIntervalAction.add(manager, createHistogramAction()); SynchronizeTraceIntervalAction.add(manager, createSynchronizeAction()); enableActions(false); // ------------- // STATUS BAR // ------------- Composite statusBar = new Composite(parent, SWT.BORDER); GridLayout statusBarLayout = new GridLayout(); GridData statusBarGridData = new GridData(); statusBarGridData.horizontalAlignment = SWT.FILL; statusBarGridData.grabExcessHorizontalSpace = true; statusBar.setLayoutData(statusBarGridData); statusBarLayout.numColumns = 1; statusBar.setLayout(statusBarLayout); // text statusText = new Text(statusBar, SWT.NONE); statusText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); statusText.setText(getStatus(0, 0)); // ---------------------- // TIME MANAGEMENT BAR // ---------------------- Composite timeComposite = new Composite(parent, SWT.BORDER); timeComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); GridLayout gl_timeComposite = new GridLayout(1, false); gl_timeComposite.horizontalSpacing = 0; timeComposite.setLayout(gl_timeComposite); // time manager timeBar = new TimeBar(timeComposite, SWT.NONE, true, true); timeBar.setEnabled(false); IStatusLineManager statusLineManager = getViewSite().getActionBars().getStatusLineManager(); timeBar.setStatusLineManager(statusLineManager); // button to synch the timeline with the table timeBar.getSynchButton().addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { timeBar.setSelection(startTimestamp, endTimestamp); } }); // load button timeBar.getLoadButton().addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { showWindow(currentShownTrace, timeBar.getStartTimestamp(), timeBar.getEndTimestamp()); } }); } private String[] getItemStrings(EventTableRow row) { String values[] = new String[EventTableColumn.values().length]; int i = 0; for (EventTableColumn col : EventTableColumn.values()) { try { values[i] = row.get(col); } catch (SoCTraceException e) { logger.error("Column " + col.getHeader() + " not found in this row."); e.printStackTrace(); } finally { i++; } } return values; } private void setColumnHeaders() { ColumnData columnData[] = new ColumnData[EventTableColumn.values().length]; int i = 0; for (EventTableColumn col : EventTableColumn.values()) { columnData[i++] = new ColumnData(col.getHeader(), col.getWidth(), SWT.LEFT); } table.setColumnHeaders(columnData); i = 0; for (EventTableColumn col : EventTableColumn.values()) { table.getColumns()[i++].setData(Key.COLUMN_OBJ, col); } } private void createHeaderEditor() { final TableEditor tableEditor = table.createTableEditor(); tableEditor.horizontalAlignment = SWT.LEFT; tableEditor.verticalAlignment = SWT.CENTER; tableEditor.grabHorizontal = true; tableEditor.minimumWidth = 50; // Handle the header row selection table.addMouseListener(new MouseAdapter() { int columnIndex; TableColumn column; TableItem item; @Override public void mouseDown(final MouseEvent event) { if (event.button != 1 || !filterEnabled) { return; } // Identify the selected row final Point point = new Point(event.x, event.y); item = table.getItem(point); // Header row selected if ((item != null) && (table.indexOf(item) == 0)) { // Identify the selected column columnIndex = -1; for (int i = 0; i < table.getColumns().length; i++) { final Rectangle rect = item.getBounds(i); if (rect.contains(point)) { columnIndex = i; break; } } if (columnIndex == -1) { return; } column = table.getColumns()[columnIndex]; // The control that will be the editor must be a child of the Table final Text newEditor = (Text) table.createTableEditorControl(Text.class); final String headerString = (String) column.getData(Key.FILTER_TXT); if (headerString != null) { newEditor.setText(headerString); } newEditor.addFocusListener(new FocusAdapter() { @Override public void focusLost(final FocusEvent e) { final boolean changed = updateHeader(newEditor.getText()); if (changed) { applyHeader(); } } }); newEditor.addKeyListener(new KeyAdapter() { @Override public void keyPressed(final KeyEvent e) { if (e.character == SWT.CR) { updateHeader(newEditor.getText()); applyHeader(); // Set focus on the table so that the next // carriage return goes to the next result EventTableView.this.table.setFocus(); } else if (e.character == SWT.ESC) { tableEditor.getEditor().dispose(); } } }); newEditor.selectAll(); newEditor.setFocus(); tableEditor.setEditor(newEditor, item, columnIndex); } } /* * returns true if the value was changed */ private boolean updateHeader(final String text) { if (text.trim().length() > 0) { try { final String regex = regexFix(text); Pattern.compile(regex); if (regex.equals(column.getData(Key.FILTER_TXT))) { tableEditor.getEditor().dispose(); return false; } EventTableColumn col = (EventTableColumn) column.getData(Key.COLUMN_OBJ); rowFilter.setFilterText(col, regex); column.setData(Key.FILTER_TXT, regex); } catch (final PatternSyntaxException ex) { tableEditor.getEditor().dispose(); MessageDialog.openError(PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getShell(), ex.getDescription(), ex.getMessage()); return false; } } else { if (column.getData(Key.FILTER_TXT) == null) { tableEditor.getEditor().dispose(); return false; } EventTableColumn col = (EventTableColumn) column.getData(Key.COLUMN_OBJ); rowFilter.setFilterText(col, ""); column.setData(Key.FILTER_TXT, null); } return true; } public String regexFix(String pattern) { String ret = pattern; // if the pattern does not contain one of the expressions .* !^ // (at the beginning) $ (at the end), then a .* is added at the // beginning and at the end of the pattern if (!(ret.indexOf(".*") >= 0 || ret.charAt(0) == '^' || ret.charAt(ret.length() - 1) == '$')) { //$NON-NLS-1$ ret = ".*" + ret + ".*"; //$NON-NLS-1$ //$NON-NLS-2$ } return ret; } private void applyHeader() { stopFilterThread(); filterMatchCount = 0; filterCheckCount = 0; table.clearAll(); table.setData(Key.FILTER_OBJ, rowFilter); table.setItemCount(1); // +1 for header row startFilterThread(); tableEditor.getEditor().dispose(); table.refresh(); } }); table.addKeyListener(new KeyAdapter() { @Override public void keyPressed(final KeyEvent e) { e.doit = false; if (e.character == SWT.ESC) { stopFilterThread(); table.refresh(); } else if (e.character == SWT.DEL) { clearFilters(); } } }); } private void setHeaderRowItemData(final TableItem item) { item.setForeground(grayColor); for (int i = 0; i < table.getColumns().length; i++) { final TableColumn column = table.getColumns()[i]; final String filter = (String) column.getData(Key.FILTER_TXT); if (filter == null) { item.setText(i, FILTER_HINT); item.setForeground(i, grayColor); item.setFont(i, table.getFont()); } else { item.setText(i, filter); item.setForeground(i, blackColor); item.setFont(i, boldFont); } } } private void createResources() { grayColor = resourceManager.createColor(ColorUtil.blend(table.getBackground().getRGB(), table.getForeground().getRGB())); blackColor = table.getDisplay().getSystemColor(SWT.COLOR_BLACK); boldFont = resourceManager.createFont(FontDescriptor.createFrom(table.getFont()).setStyle( SWT.BOLD)); } private String getStatus(int events, int matched) { StringBuilder sb = new StringBuilder(); sb.append("Filter matched "); sb.append(matched); sb.append(" of "); sb.append(events); sb.append(" loaded events"); return sb.toString(); } protected TraceIntervalDescriptor getIntervalDescriptor() { if (currentShownTrace == null) return null; TraceIntervalDescriptor des = new TraceIntervalDescriptor(); des.setTrace(currentShownTrace); des.setStartTimestamp(startTimestamp); des.setEndTimestamp(endTimestamp); return des; } private IAction createColumnAction() { IAction colAction = new Action("Adjust Columns", Action.AS_PUSH_BUTTON) { @Override public void run() { for (TableColumn column : table.getColumns()) { EventTableColumn col = (EventTableColumn) column.getData(Key.COLUMN_OBJ); column.pack(); if (column.getWidth() < col.getWidth()) column.setWidth(col.getWidth()); } } }; colAction.setImageDescriptor(ResourceManager.getPluginImageDescriptor(Activator.PLUGIN_ID, "icons/adjust_h.png")); colAction.setToolTipText("Adjust column size to content"); return colAction; } private IAction createCSVExportAction() { CSVExportAction csvAction = new CSVExportAction("Export to CSV", Action.AS_PUSH_BUTTON); csvAction.tableView = this; csvAction.setImageDescriptor(ResourceManager.getPluginImageDescriptor(Activator.PLUGIN_ID, "icons/export.png")); csvAction.setToolTipText("Export currently displayed events to CSV"); return csvAction; } private class CSVExportAction extends Action { public EventTableView tableView; public CSVExportAction(String string, int asPushButton) { super(string, asPushButton); } @Override public void run() { columnSelection = new HashMap<EventTableColumn, Boolean>(); // Set a default file name exportFileName = currentShownTrace.getAlias() + "_" + currentShownTrace.getId() + ".csv"; CSVExportDialog csvExportDialog = new CSVExportDialog(getSite().getShell(), tableView); if (csvExportDialog.open() == Status.OK) { startExport(exportFileName, columnSelection); } } } @Override public void setFocus() { if (table != null) table.setFocus(); super.setFocus(); } @Override public void dispose() { stopFilterThread(); stopDrawerThread(); stopExport(); if (loaderJob != null) loaderJob.cancel(); if (table != null) table.dispose(); table = null; if (timeBar != null) timeBar.dispose(); timeBar = null; resourceManager.dispose(); super.dispose(); } @Override public String getId() { return ID; } public Map<EventTableColumn, Boolean> getColumnSelection() { return columnSelection; } public void setColumnSelection(Map<EventTableColumn, Boolean> columnSelection) { this.columnSelection = columnSelection; } public String getExportFileName() { return exportFileName; } public void setExportFileName(String exportFileName) { this.exportFileName = exportFileName; } /** * Show the trace. * * @param data * if null, all the trace must be loaded. Otherwise data is a * TraceIntervalDescriptor. */ @Override public void showTrace(final Trace trace, final Object data) { if (data == null) { showWindow(trace, trace.getMinTimestamp(), trace.getMaxTimestamp()); } else { TraceIntervalDescriptor des = (TraceIntervalDescriptor) data; // Are we in the case where we should focus on an event if (data instanceof EventTableDescriptor) { focusOnEvent = true; focusEventDescriptor = (EventTableDescriptor) data; } showWindow(des.getTrace(), des.getStartTimestamp(), des.getEndTimestamp()); } } @Override public void partHandle(FramesocBusTopic topic, Object data) { logger.debug("nothing to do here"); } /* * Loading */ private void showWindow(Trace trace, long start, long end) { if (trace == null) { return; } // create the queue LoaderQueue<Event> queue = new LoaderQueue<>(); // create the event loader IEventLoader loader = new EventLoader(); loader.setTrace(trace); loader.setQueue(queue); // compute the actual time interval to load (trim with trace min and max) TimeInterval interval = new TimeInterval(start, end); interval.startTimestamp = Math.max(trace.getMinTimestamp(), interval.startTimestamp); interval.endTimestamp = Math.min(trace.getMaxTimestamp(), interval.endTimestamp); timeBar.setExtrema(trace.getMinTimestamp(), trace.getMaxTimestamp()); timeBar.setDisplayInterval(interval); // clear the filters clearFilters(); // check for contained interval if (trace.equals(currentShownTrace) && cache.contains(interval)) { cache.index(interval); table.clearAll(); table.setItemCount(cache.getIndexedRowCount() + 1); // +1 for header table.refresh(); if (!focusOnEvent) { table.setSelection(0); } else { focusOnEvent = false; table.setSelection(findEventIndex()); } startTimestamp = interval.startTimestamp; endTimestamp = interval.endTimestamp; statusText.setText(getStatus(cache.getIndexedRowCount(), cache.getIndexedRowCount())); timeBar.setSelection(interval.startTimestamp, interval.endTimestamp); return; } // launch the job loading the queue launchLoaderJob(loader, interval); // update the viewer startDrawerThread(interval, trace, queue); } private void launchLoaderJob(final IEventLoader loader, final TimeInterval requestedInterval) { loaderJob = new Job("Loading Event Table...") { @Override protected IStatus run(IProgressMonitor monitor) { DeltaManager all = new DeltaManager(); all.start(); loader.loadWindow(requestedInterval.startTimestamp, requestedInterval.endTimestamp, monitor); logger.debug(all.endMessage("Loader Job: loaded everything")); if (monitor.isCanceled()) return Status.CANCEL_STATUS; return Status.OK_STATUS; } }; loaderJob.setUser(false); loaderJob.schedule(); } private void startDrawerThread(final TimeInterval requestedInterval, final Trace trace, final LoaderQueue<Event> queue) { currentShownTrace = trace; timeBar.setMinTimestamp(currentShownTrace.getMinTimestamp()); timeBar.setMaxTimestamp(currentShownTrace.getMaxTimestamp()); table.clearAll(); cache.clear(); stopDrawerThread(); drawerThread = new DrawerThread(requestedInterval, queue); drawerThread.start(); } private void stopDrawerThread() { if (drawerThread != null) { drawerThread.cancel(); drawerThread = null; } } /** * Wrapper Thread object for the filtering thread. */ private class DrawerThread extends Thread { private TimeInterval requestedInterval; private LoaderQueue<Event> queue; private volatile boolean stop = false; private boolean refreshBusy = false; private boolean refreshPending = false; private Object syncObj = new Object(); public DrawerThread(TimeInterval requestedInterval, LoaderQueue<Event> queue) { this.requestedInterval = requestedInterval; this.queue = queue; } @Override public void run() { DeltaManager all = new DeltaManager(); all.start(); endTimestamp = Long.MIN_VALUE; startTimestamp = Long.MAX_VALUE; filterEnabled = false; enableWidgets(false); while (!queue.done()) { try { List<Event> events = queue.pop(); if (events.isEmpty()) continue; // put the events in the cache for (Event e : events) { if (stop) break; cache.put(new EventTableRow(e)); startTimestamp = Math.max(requestedInterval.startTimestamp, Math.min(e.getTimestamp(), startTimestamp)); endTimestamp = Math.min(requestedInterval.endTimestamp, Math.max(e.getTimestamp(), endTimestamp)); refreshTable(); } } catch (InterruptedException e) { logger.debug("Interrupted while taking the queue head"); } } if (!queue.isStop() && !stop) { // we have not been stopped: the requested interval has been displayed startTimestamp = requestedInterval.startTimestamp; endTimestamp = requestedInterval.endTimestamp; cache.setRequestedInterval(requestedInterval); // refresh one last time refreshTable(); enableWidgets(true); } else { // we have been stopped: something has not been displayed in the table startTimestamp = Math.max(requestedInterval.startTimestamp, startTimestamp); endTimestamp = Math.min(requestedInterval.endTimestamp, endTimestamp); if (cache.getIndexedRowCount() > 0) { // refresh one last time refreshTable(); enableWidgets(true); } else { closeView(); } } // Do we set the focus on a specific event if (focusOnEvent) { Display.getDefault().syncExec(new Runnable() { @Override public void run() { focusOnEvent = false; table.setSelection(findEventIndex()); } }); } logger.debug(all.endMessage("Drawer Thread: visualizing everything")); logger.debug("start: {}", startTimestamp); logger.debug("end: {}", endTimestamp); } public void cancel() { stop = true; } private void closeView() { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { FramesocPartManager.getInstance().disposeFramesocPart(EventTableView.this); EventTableView.this.hideView(); } }); } private void refreshTable() { synchronized (syncObj) { if (refreshBusy) { refreshPending = true; return; } refreshBusy = true; } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { if (stop) return; if (table.isDisposed() || statusText.isDisposed() || timeBar.isDisposed()) { return; } int events = cache.getIndexedRowCount(); table.setItemCount(events + 1); // +1 for header row table.refresh(); timeBar.setTimeUnit(TimeUnit.getTimeUnit(currentShownTrace.getTimeUnit())); timeBar.setSelection(startTimestamp, endTimestamp); timeBar.setDisplayInterval(startTimestamp, endTimestamp); statusText.setText(getStatus(events, events)); synchronized (syncObj) { refreshBusy = false; if (refreshPending) { refreshPending = false; refreshTable(); } } } }); } private void enableWidgets(final boolean enabled) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { filterEnabled = enabled; if (timeBar.isDisposed()) { return; } timeBar.setEnabled(enabled); enableActions(enabled); } }); } } /* * Filtering */ /** * Wrapper Thread object for the filtering thread. */ private class FilterThread extends Thread { private volatile boolean stop = false; private EventTableRowFilter filter; private boolean refreshBusy = false; private boolean refreshPending = false; private final Object syncObj = new Object(); /** * Constructor. */ public FilterThread(EventTableRowFilter filter) { super("Filter Thread"); //$NON-NLS-1$ this.filter = filter; } @Override public void run() { if (currentShownTrace == null) { return; } filterMatchCount = 0; filterCheckCount = 0; cache.index(); int activeRows = cache.getIndexedRowCount(); for (int i = 0; i < activeRows; i++) { if (stop) { break; } EventTableRow r = cache.get(i); if (filter.matches(r)) { cache.remap(r, filterMatchCount++); refreshTable(); } else if ((filterCheckCount % 100) == 0) { refreshTable(); } filterCheckCount++; } refreshTable(); cache.cleanIndex(filterMatchCount); synchronized (filterSyncObj) { filterThread = null; } } /** * Refresh the table. */ public void refreshTable() { synchronized (syncObj) { if (refreshBusy) { refreshPending = true; return; } refreshBusy = true; } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { if (stop) { return; } if (table.isDisposed() || statusText.isDisposed()) { return; } table.setItemCount(filterMatchCount + 1); // +1 for header row table.refresh(); statusText.setText(getStatus(filterCheckCount, filterMatchCount)); synchronized (syncObj) { refreshBusy = false; if (refreshPending) { refreshPending = false; refreshTable(); } } } }); } /** * Cancel this filtering thread. */ public void cancel() { stop = true; } } /** * Start the filtering thread. */ private void startFilterThread() { synchronized (filterSyncObj) { if (filterThread != null) { filterThread.cancel(); filterThread = null; } filterThread = new FilterThread(rowFilter); filterThread.start(); } } /** * Stop the filtering thread. */ private void stopFilterThread() { synchronized (filterSyncObj) { if (filterThread != null) { filterThread.cancel(); filterThread = null; } } } /** * Clear all currently active filters. */ private void clearFilters() { if (table.getData(Key.FILTER_OBJ) == null) { return; } stopFilterThread(); rowFilter.clean(); table.clearAll(); for (final TableColumn column : table.getColumns()) { column.setData(Key.FILTER_TXT, null); } table.setData(Key.FILTER_OBJ, null); if (currentShownTrace != null) { cache.index(); table.setItemCount(cache.getIndexedRowCount() + 1); // +1 for header row } else { table.setItemCount(1); // +1 for header row } filterMatchCount = 0; filterCheckCount = 0; table.setSelection(0); statusText.setText(getStatus(cache.getIndexedRowCount(), cache.getIndexedRowCount())); } /** * Start the export */ private void startExport(String fileName, Map<EventTableColumn, Boolean> exportColumn) { synchronized (exportSyncObj) { if (csvExport != null) { csvExport.cancel(); csvExport = null; } csvExport = new CSVExport(fileName, exportColumn, this.cache); csvExport.exportToCSV(); } } /** * Stop the export */ private void stopExport() { synchronized (exportSyncObj) { if (csvExport != null) { csvExport.cancel(); csvExport = null; } } } /** * From a selected row of the table, open the Gantt chart and center on the * event of the selected row by loading at least 1% of the trace around it * * @param row * the selected row */ private void switchToGantt(EventTableRow row) { if (row == null) return; long startTimestamp = row.getTimestamp(); long endTimestamp = startTimestamp; EventProducer ep = null; try { String eventProducer = row.get(EventTableColumn.PRODUCER_NAME); String cpu = row.get(EventTableColumn.CPU); ep = findEventProducer(eventProducer, cpu); int eventCat = EventCategory.stringToCategory(row .get(EventTableColumn.CATEGORY)); switch (eventCat) { case EventCategory.STATE: case EventCategory.LINK: // Get the end timestamp String info = row.get(EventTableColumn.PARAMS); String[] params = info.split(EventTableRow.PARAMETER_SEPARATOR); String[] endTimestamps = params[0] .split(EventTableRow.PARAMETER_VALUE_SEPARATOR); endTimestamp = Long.valueOf(endTimestamps[1] .split(EventTableRow.PARAMETER_VALUE_ESCAPE)[1]); break; default: break; } } catch (NumberFormatException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SoCTraceException e) { // TODO Auto-generated catch block e.printStackTrace(); } long duration = endTimestamp - startTimestamp; // Punctual event if (duration == 0) // Show only the minimum between MIN_TIME_UNIT_SHOWING time units around or one // percent of the trace duration = Math.min(TraceConfigurationDescriptor.MIN_TIME_UNIT_SHOWING, (currentShownTrace.getMaxTimestamp() - currentShownTrace .getMinTimestamp()) / 100); TraceConfigurationDescriptor des = new TraceConfigurationDescriptor(); des.setTrace(currentShownTrace); des.setStartTimestamp(startTimestamp - duration / 2); des.setEndTimestamp(endTimestamp + duration / 2); des.setEventProducer(ep); FramesocBus.getInstance().send( FramesocBusTopic.TOPIC_UI_GANTT_DISPLAY_TIME_INTERVAL, des); } /** * Find an event producer based on its name. Very error-prone in case of * multiple event producers with the same name but the table does not * provide more information on the producer than the name. TODO Study the * possibility to make more accurate the research based on CPU * * @param eventProducerName * the name of the event producer to be found * @param cpu * the cpu it should belong to * @return the found event producer, null otherwise */ private EventProducer findEventProducer(String eventProducerName, String cpu) { TraceDBObject traceDB; try { // Open the trace database traceDB = TraceDBObject.openNewInstance(currentShownTrace .getDbName()); // Get the producers EventProducerQuery pq = new EventProducerQuery(traceDB); List<EventProducer> producers = pq.getList(); for (EventProducer anEP : producers) { // If one producer is found with the same name if (anEP.getName().equals(eventProducerName)) return anEP; } } catch (SoCTraceException e) { // TODO Auto-generated catch block e.printStackTrace(); } logger.debug("No event producer with the name " + eventProducerName + " was found."); return null; } /** * Find the first event corresponding to the info given in the * EventTableDescriptor * * @return the corresponding index if found, 0 (header) otherwise */ private int findEventIndex() { int index = 0; try { for (int i = 0; i < cache.getIndexedRowCount(); i++) { EventTableRow row = cache.get(i); if (row.getTimestamp() != focusEventDescriptor .getEventStartTimeStamp()) continue; if (!row.get(EventTableColumn.TYPE_NAME).equals( focusEventDescriptor.getTypeName())) continue; if (!row.get(EventTableColumn.PRODUCER_NAME).equals( focusEventDescriptor.getEventProducerName())) continue; // Add +1 because of the header row index = i + 1; break; } } catch (SoCTraceException e) { // TODO Auto-generated catch block e.printStackTrace(); } return index; } }