From f11626cebc96b4716f188711e943ea4aeed7d720 Mon Sep 17 00:00:00 2001 From: Alejandro Serrano Date: Thu, 21 Jul 2011 14:34:19 +0200 Subject: [PATCH] Mostly finished profiling ouput viewer. --- .../META-INF/MANIFEST.MF | 4 +- .../internal/editors/ChartCanvas.java | 206 ++++++++++++++++++ .../editors/ChartWithToolTipCanvas.java | 103 +++++++++ .../internal/editors/ProfileNumbers.java | 49 +++++ .../internal/editors/ProfilerViewer.java | 138 +++++++++++- 5 files changed, 487 insertions(+), 13 deletions(-) create mode 100644 net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ChartCanvas.java create mode 100644 net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ChartWithToolTipCanvas.java create mode 100644 net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ProfileNumbers.java diff --git a/net.sf.eclipsefp.haskell.profiler/META-INF/MANIFEST.MF b/net.sf.eclipsefp.haskell.profiler/META-INF/MANIFEST.MF index 4435f0a71..8b12dc90a 100644 --- a/net.sf.eclipsefp.haskell.profiler/META-INF/MANIFEST.MF +++ b/net.sf.eclipsefp.haskell.profiler/META-INF/MANIFEST.MF @@ -8,7 +8,9 @@ Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, org.eclipse.team.core, org.eclipse.core.resources, - org.eclipse.ui.ide + org.eclipse.ui.ide, + org.eclipse.birt.chart.device.swt, + org.eclipse.birt.chart.ui Eclipse-LazyStart: true Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-Vendor: %bundleVendor diff --git a/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ChartCanvas.java b/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ChartCanvas.java new file mode 100644 index 000000000..e7c461bbc --- /dev/null +++ b/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ChartCanvas.java @@ -0,0 +1,206 @@ +/******************************************************************************* + * Copyright (c) 2006, 2010 IBM Corporation 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: + * Qi Liang (IBM Corporation) + * + * Modified as explained in + * http://www.eclipse.org/forums/index.php/mv/msg/171310/557779/#msg_557779 +*******************************************************************************/ +package net.sf.eclipsefp.haskell.profiler.internal.editors; + +import org.eclipse.birt.chart.device.IDeviceRenderer; +import org.eclipse.birt.chart.exception.ChartException; +import org.eclipse.birt.chart.factory.GeneratedChartState; +import org.eclipse.birt.chart.factory.Generator; +import org.eclipse.birt.chart.model.Chart; +import org.eclipse.birt.chart.model.attribute.Bounds; +import org.eclipse.birt.chart.model.attribute.impl.BoundsImpl; +import org.eclipse.birt.chart.util.PluginSettings; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; + +/** + * The canvas to show chart. + * + * @author Qi Liang + */ +public class ChartCanvas extends Canvas { + + /** + * The device render for rendering chart. + */ + protected IDeviceRenderer render = null; + + /** + * The chart instantce. + */ + protected Chart chart = null; + + /** + * The chart state. + */ + protected GeneratedChartState state = null; + + /** + * The image which caches the chart image to improve drawing performance. + */ + private Image cachedImage = null; + + /** + * Constructs one canvas containing chart. + * + * @param parent + * a composite control which will be the parent of the new + * instance (cannot be null) + * @param style + * the style of control to construct + */ + public ChartCanvas(Composite parent, int style) { + super(parent, style); + + // initialize the SWT rendering device + try { + PluginSettings ps = PluginSettings.instance(); + render = ps.getDevice("dv.SWT"); + } catch (ChartException ex) { + ex.printStackTrace(); + } + + addPaintListener(new PaintListener() { + + public void paintControl(PaintEvent e) { + + Composite co = (Composite) e.getSource(); + final Rectangle rect = co.getClientArea(); + + render.setProperty(IDeviceRenderer.GRAPHICS_CONTEXT, e.gc); + + if (cachedImage == null) { + buildChart(); + drawToCachedImage(rect); + } + e.gc.drawImage(cachedImage, + 0, + 0, + cachedImage.getBounds().width, + cachedImage.getBounds().height, + 0, + 0, + rect.width, + rect.height); + + } + }); + + addControlListener(new ControlAdapter() { + + public void controlResized(ControlEvent e) { + + render.setProperty(IDeviceRenderer.GRAPHICS_CONTEXT, new GC(ChartCanvas.this)); + buildChart(); + cachedImage = null; + } + }); + } + + /** + * Builds the chart state. This method should be call when data is changed. + */ + private void buildChart() { + Point size = getSize(); + Bounds bo = BoundsImpl.create(0, 0, size.x, size.y); + int resolution = render.getDisplayServer().getDpiResolution(); + bo.scale(72d / resolution); + try { + Generator gr = Generator.instance(); + state = gr.build(render.getDisplayServer(), + chart, + bo, + null, + null, + null); + } catch (ChartException ex) { + ex.printStackTrace(); + } + } + + /** + * Draws the chart onto the cached image in the area of the given + * Rectangle. + * + * @param size + * the area to draw + */ + public void drawToCachedImage(Rectangle size) { + GC gc = null; + try { + if (cachedImage != null) + cachedImage.dispose(); + cachedImage = new Image(Display.getCurrent(), size.width, + size.height); + + gc = new GC(cachedImage); + render.setProperty(IDeviceRenderer.GRAPHICS_CONTEXT, gc); + + Generator gr = Generator.instance(); + + gr.render(render, state); + } catch (ChartException ex) { + ex.printStackTrace(); + } finally { + if (gc != null) + gc.dispose(); + } + } + + /** + * Returns the chart which is contained in this canvas. + * + * @return the chart contained in this canvas. + */ + public Chart getChart() { + return chart; + } + + /** + * Sets the chart into this canvas. Note: When the chart is set, the cached + * image will be dopped, but this method doesn't reset the flag + * cachedImage. + * + * @param chart + * the chart to set + */ + public void setChart(Chart chart) { + if (cachedImage != null) + cachedImage.dispose(); + + cachedImage = null; + this.chart = chart; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.swt.widgets.Widget#dispose() + */ + public void dispose() { + if (cachedImage != null) + cachedImage.dispose(); + super.dispose(); + } + +} \ No newline at end of file diff --git a/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ChartWithToolTipCanvas.java b/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ChartWithToolTipCanvas.java new file mode 100644 index 000000000..e8ff3fff8 --- /dev/null +++ b/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ChartWithToolTipCanvas.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2006, 2010 IBM Corporation 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: + * Qi Liang (IBM Corporation) +*******************************************************************************/ +package net.sf.eclipsefp.haskell.profiler.internal.editors; + +import org.eclipse.birt.chart.device.IDeviceRenderer; +import org.eclipse.birt.chart.device.IUpdateNotifier; +import org.eclipse.birt.chart.model.Chart; +import org.eclipse.swt.widgets.Composite; + +/** + * The canvas to draw chart with the tool tip to show the value. + * + * @author Qi Liang + */ +public class ChartWithToolTipCanvas extends ChartCanvas implements + IUpdateNotifier { + + public ChartWithToolTipCanvas(Composite parent, int style) { + super(parent, style); + render.setProperty(IDeviceRenderer.UPDATE_NOTIFIER, this); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#regenerateChart() + */ + public void regenerateChart() { + redraw(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#repaintChart() + */ + public void repaintChart() { + redraw(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#peerInstance() + */ + public Object peerInstance() { + return this; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#getDesignTimeModel() + */ + public Chart getDesignTimeModel() { + return chart; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#getRunTimeModel() + */ + public Chart getRunTimeModel() { + return state.getChartModel(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#getContext(java.lang.Object) + */ + public Object getContext(Object arg0) { + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#putContext(java.lang.Object, + * java.lang.Object) + */ + public Object putContext(Object arg0, Object arg1) { + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#removeContext(java.lang.Object) + */ + public Object removeContext(Object arg0) { + return null; + } +} \ No newline at end of file diff --git a/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ProfileNumbers.java b/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ProfileNumbers.java new file mode 100644 index 000000000..2ace13495 --- /dev/null +++ b/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ProfileNumbers.java @@ -0,0 +1,49 @@ +package net.sf.eclipsefp.haskell.profiler.internal.editors; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import net.sf.eclipsefp.haskell.profiler.model.Job; +import net.sf.eclipsefp.haskell.profiler.model.Sample; + +public class ProfileNumbers { + + private LinkedHashMap entries; + private double[] rest; + + public ProfileNumbers(List> entriesApart, int noSamples) { + this.entries = new LinkedHashMap(); + for (Map.Entry e : entriesApart) { + double[] values = new double[noSamples]; + Arrays.fill(values, 0.0); + entries.put(e.getKey(), values); + } + this.rest = new double[noSamples]; + Arrays.fill(this.rest, 0.0); + } + + public void fillIn(Job job) { + int sampleNo = 0; + for (Sample s : job.getSamples()) { + for (Map.Entry e : s.getEntries()) { + if (entries.containsKey(e.getKey())) { + entries.get(e.getKey())[sampleNo] = e.getValue(); + } else { + rest[sampleNo] += e.getValue(); + } + } + sampleNo++; + } + } + + public LinkedHashMap getEntries() { + return entries; + } + + public double[] getRest() { + return rest; + } +} diff --git a/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ProfilerViewer.java b/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ProfilerViewer.java index a04e2b099..eaa788cd0 100644 --- a/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ProfilerViewer.java +++ b/net.sf.eclipsefp.haskell.profiler/src/net/sf/eclipsefp/haskell/profiler/internal/editors/ProfilerViewer.java @@ -2,13 +2,35 @@ import java.io.InputStream; import java.math.BigInteger; +import java.util.Collections; +import java.util.List; import java.util.Map; import net.sf.eclipsefp.haskell.profiler.model.Job; +import net.sf.eclipsefp.haskell.profiler.model.Sample; +import org.eclipse.birt.chart.model.Chart; +import org.eclipse.birt.chart.model.ChartWithAxes; +import org.eclipse.birt.chart.model.attribute.LegendItemType; +import org.eclipse.birt.chart.model.attribute.TickStyle; +import org.eclipse.birt.chart.model.component.Axis; +import org.eclipse.birt.chart.model.component.Series; +import org.eclipse.birt.chart.model.component.impl.SeriesImpl; +import org.eclipse.birt.chart.model.data.NumberDataSet; +import org.eclipse.birt.chart.model.data.SeriesDefinition; +import org.eclipse.birt.chart.model.data.impl.NumberDataSetImpl; +import org.eclipse.birt.chart.model.data.impl.SeriesDefinitionImpl; +import org.eclipse.birt.chart.model.impl.ChartWithAxesImpl; +import org.eclipse.birt.chart.model.type.AreaSeries; +import org.eclipse.birt.chart.model.type.impl.AreaSeriesImpl; +import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Status; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IFileEditorInput; @@ -18,32 +40,126 @@ public class ProfilerViewer extends EditorPart { Job job = null; - + double[] samplePoints; + List> entries; + ChartCanvas canvas; + public ProfilerViewer() { super(); } - + @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { + setSite(site); + setInput(input); + try { - IFileEditorInput fInput = (IFileEditorInput)input; - InputStream contents = fInput.getFile().getContents(); + IFileEditorInput fInput = (IFileEditorInput) input; + IFile inputFile = fInput.getFile(); + setPartName(inputFile.getName()); + InputStream contents = inputFile.getContents(); job = Job.parse(contents); contents.close(); - for(Map.Entry entry : job.sortEntriesByTotal()) { - System.out.print(entry.getKey()); - System.out.print(": "); - System.out.println(entry.getValue().toString()); + // Sort entries + entries = job.sortEntriesByTotal(); + // Get sample points + samplePoints = new double[job.getSamplesAndTimes().size()]; + int i = 0; + for (Sample s : job.getSamples()) { + samplePoints[i] = s.getTime(); + i++; } } catch (Exception e) { throw new PartInitException(Status.CANCEL_STATUS); } } - + @Override public void createPartControl(Composite parent) { - // TODO Auto-generated method stub + GridLayout layout = new GridLayout(); + layout.numColumns = 1; + parent.setLayout(layout); + + Label l = new Label(parent, SWT.NONE); + l.setText("Placeholder"); + GridData lGridData = new GridData(GridData.FILL_HORIZONTAL); + l.setLayoutData(lGridData); + + Chart chart = createChart(15); + canvas = new ChartCanvas(parent, SWT.NONE); + canvas.setChart(chart); + GridData cGridData = new GridData(GridData.FILL_BOTH); + canvas.setLayoutData(cGridData); + } + + private Chart createChart(int numberApart) { + int n = entries.size() < numberApart ? entries.size() : numberApart; + List> entriesApart = entries.subList(0, n); + + Chart chart = ChartWithAxesImpl.create(); + // Title + chart.getTitle().getLabel().getCaption().setValue(job.getName()); + // chart.getTitle().getLabel().getCaption().getFont().setSize(14); + // chart.getTitle().getLabel().getCaption().getFont().setName("Arial"); + // Legend + chart.getLegend().setItemType(LegendItemType.SERIES_LITERAL); + chart.getLegend().setVisible(true); + // X-Axis -> time + Axis xAxis = ((ChartWithAxes) chart).getPrimaryBaseAxes()[0]; + // xAxis.setType(AxisType.LINEAR_LITERAL); + xAxis.getMajorGrid().setTickStyle(TickStyle.BELOW_LITERAL); + xAxis.getTitle().setVisible(true); + xAxis.getTitle().getCaption().setValue(job.getSampleUnit()); + xAxis.getLabel().setVisible(true); + // X-Axis data + NumberDataSet xDataSet = NumberDataSetImpl.create(samplePoints); + Series xCategory = SeriesImpl.create(); + xCategory.setDataSet(xDataSet); + SeriesDefinition sdX = SeriesDefinitionImpl.create(); + sdX.getSeriesPalette().shift(0); + xAxis.getSeriesDefinitions().add(sdX); + sdX.getSeries().add(xCategory); + // Y-Axis -> memory + Axis yAxis = ((ChartWithAxes) chart).getPrimaryOrthogonalAxis(xAxis); + yAxis.getMajorGrid().setTickStyle(TickStyle.LEFT_LITERAL); + yAxis.getMajorGrid().getLineAttributes().setVisible(true); + yAxis.getMinorGrid().getLineAttributes().setVisible(true); + yAxis.setPercent(false); + yAxis.getTitle().getCaption().setValue(job.getValueUnit()); + yAxis.getTitle().setVisible(true); + yAxis.getTitle().getCaption().getFont().setRotation(90); + yAxis.getLabel().setVisible(true); + // Y-Axis data + SeriesDefinition sdY = SeriesDefinitionImpl.create(); + sdY.getSeriesPalette().shift(1); + yAxis.getSeriesDefinitions().add(sdY); + // Get the numbers + ProfileNumbers numbers = new ProfileNumbers(entriesApart, samplePoints.length); + numbers.fillIn(job); + // Add (rest) elements + NumberDataSet restDataSet = NumberDataSetImpl.create(numbers.getRest()); + AreaSeries restSeries = (AreaSeries) AreaSeriesImpl.create(); + restSeries.setSeriesIdentifier("(rest)"); + restSeries.setDataSet(restDataSet); + restSeries.getLineAttributes().setVisible(false); + restSeries.getLabel().setVisible(false); + restSeries.setStacked(true); + sdY.getSeries().add(restSeries); + // Add apart elements, in reverse order + Collections.reverse(entriesApart); + for (Map.Entry entry : entriesApart) { + double[] entryNumbers = numbers.getEntries().get(entry.getKey()); + NumberDataSet entryDataSet = NumberDataSetImpl.create(entryNumbers); + AreaSeries entrySeries = (AreaSeries) AreaSeriesImpl.create(); + entrySeries.setSeriesIdentifier(entry.getKey()); + entrySeries.setDataSet(entryDataSet); + entrySeries.getLineAttributes().setVisible(false); + entrySeries.getLabel().setVisible(false); + entrySeries.setStacked(true); + sdY.getSeries().add(entrySeries); + } + return chart; } @Override @@ -71,6 +187,4 @@ public boolean isSaveAsAllowed() { return false; } - - }