• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.traceview;
18 
19 import org.eclipse.jface.resource.FontRegistry;
20 import org.eclipse.swt.SWT;
21 import org.eclipse.swt.custom.SashForm;
22 import org.eclipse.swt.events.MouseAdapter;
23 import org.eclipse.swt.events.MouseEvent;
24 import org.eclipse.swt.events.MouseMoveListener;
25 import org.eclipse.swt.events.MouseWheelListener;
26 import org.eclipse.swt.events.PaintEvent;
27 import org.eclipse.swt.events.PaintListener;
28 import org.eclipse.swt.graphics.Color;
29 import org.eclipse.swt.graphics.Cursor;
30 import org.eclipse.swt.graphics.FontData;
31 import org.eclipse.swt.graphics.GC;
32 import org.eclipse.swt.graphics.Image;
33 import org.eclipse.swt.graphics.Point;
34 import org.eclipse.swt.graphics.Rectangle;
35 import org.eclipse.swt.layout.FillLayout;
36 import org.eclipse.swt.layout.GridData;
37 import org.eclipse.swt.layout.GridLayout;
38 import org.eclipse.swt.widgets.Canvas;
39 import org.eclipse.swt.widgets.Composite;
40 import org.eclipse.swt.widgets.Display;
41 import org.eclipse.swt.widgets.Event;
42 import org.eclipse.swt.widgets.Listener;
43 import org.eclipse.swt.widgets.ScrollBar;
44 
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Collection;
48 import java.util.Collections;
49 import java.util.Comparator;
50 import java.util.HashMap;
51 import java.util.Observable;
52 import java.util.Observer;
53 
54 public class TimeLineView extends Composite implements Observer {
55 
56     private HashMap<String, RowData> mRowByName;
57     private RowData[] mRows;
58     private Segment[] mSegments;
59     private HashMap<Integer, String> mThreadLabels;
60     private Timescale mTimescale;
61     private Surface mSurface;
62     private RowLabels mLabels;
63     private SashForm mSashForm;
64     private int mScrollOffsetY;
65 
66     public static final int PixelsPerTick = 50;
67     private TickScaler mScaleInfo = new TickScaler(0, 0, 0, PixelsPerTick);
68     private static final int LeftMargin = 10; // blank space on left
69     private static final int RightMargin = 60; // blank space on right
70 
71     private Color mColorBlack;
72     private Color mColorGray;
73     private Color mColorDarkGray;
74     private Color mColorForeground;
75     private Color mColorRowBack;
76     private Color mColorZoomSelection;
77     private FontRegistry mFontRegistry;
78 
79     /** vertical height of drawn blocks in each row */
80     private static final int rowHeight = 20;
81 
82     /** the blank space between rows */
83     private static final int rowYMargin = 12;
84     private static final int rowYMarginHalf = rowYMargin / 2;
85 
86     /** total vertical space for row */
87     private static final int rowYSpace = rowHeight + rowYMargin;
88     private static final int majorTickLength = 8;
89     private static final int minorTickLength = 4;
90     private static final int timeLineOffsetY = 58;
91     private static final int tickToFontSpacing = 2;
92 
93     /** start of first row */
94     private static final int topMargin = 90;
95     private int mMouseRow = -1;
96     private int mNumRows;
97     private int mStartRow;
98     private int mEndRow;
99     private TraceUnits mUnits;
100     private String mClockSource;
101     private boolean mHaveCpuTime;
102     private boolean mHaveRealTime;
103     private int mSmallFontWidth;
104     private int mSmallFontHeight;
105     private SelectionController mSelectionController;
106     private MethodData mHighlightMethodData;
107     private Call mHighlightCall;
108     private static final int MinInclusiveRange = 3;
109 
110     /** Setting the fonts looks good on Linux but bad on Macs */
111     private boolean mSetFonts = false;
112 
113     public static interface Block {
getName()114         public String getName();
getMethodData()115         public MethodData getMethodData();
getStartTime()116         public long getStartTime();
getEndTime()117         public long getEndTime();
getColor()118         public Color getColor();
addWeight(int x, int y, double weight)119         public double addWeight(int x, int y, double weight);
clearWeight()120         public void clearWeight();
getExclusiveCpuTime()121         public long getExclusiveCpuTime();
getInclusiveCpuTime()122         public long getInclusiveCpuTime();
getExclusiveRealTime()123         public long getExclusiveRealTime();
getInclusiveRealTime()124         public long getInclusiveRealTime();
isContextSwitch()125         public boolean isContextSwitch();
isIgnoredBlock()126         public boolean isIgnoredBlock();
getParentBlock()127         public Block getParentBlock();
128     }
129 
130     public static interface Row {
getId()131         public int getId();
getName()132         public String getName();
133     }
134 
135     public static class Record {
136         Row row;
137         Block block;
138 
Record(Row row, Block block)139         public Record(Row row, Block block) {
140             this.row = row;
141             this.block = block;
142         }
143     }
144 
TimeLineView(Composite parent, TraceReader reader, SelectionController selectionController)145     public TimeLineView(Composite parent, TraceReader reader,
146             SelectionController selectionController) {
147         super(parent, SWT.NONE);
148         mRowByName = new HashMap<String, RowData>();
149         this.mSelectionController = selectionController;
150         selectionController.addObserver(this);
151         mUnits = reader.getTraceUnits();
152         mClockSource = reader.getClockSource();
153         mHaveCpuTime = reader.haveCpuTime();
154         mHaveRealTime = reader.haveRealTime();
155         mThreadLabels = reader.getThreadLabels();
156 
157         Display display = getDisplay();
158         mColorGray = display.getSystemColor(SWT.COLOR_GRAY);
159         mColorDarkGray = display.getSystemColor(SWT.COLOR_DARK_GRAY);
160         mColorBlack = display.getSystemColor(SWT.COLOR_BLACK);
161         // mColorBackground = display.getSystemColor(SWT.COLOR_WHITE);
162         mColorForeground = display.getSystemColor(SWT.COLOR_BLACK);
163         mColorRowBack = new Color(display, 240, 240, 255);
164         mColorZoomSelection = new Color(display, 230, 230, 230);
165 
166         mFontRegistry = new FontRegistry(display);
167         mFontRegistry.put("small",  //$NON-NLS-1$
168                 new FontData[] { new FontData("Arial", 8, SWT.NORMAL) });  //$NON-NLS-1$
169         mFontRegistry.put("courier8",  //$NON-NLS-1$
170                 new FontData[] { new FontData("Courier New", 8, SWT.BOLD) });  //$NON-NLS-1$
171         mFontRegistry.put("medium",  //$NON-NLS-1$
172                 new FontData[] { new FontData("Courier New", 10, SWT.NORMAL) });  //$NON-NLS-1$
173 
174         Image image = new Image(display, new Rectangle(100, 100, 100, 100));
175         GC gc = new GC(image);
176         if (mSetFonts) {
177             gc.setFont(mFontRegistry.get("small"));  //$NON-NLS-1$
178         }
179         mSmallFontWidth = gc.getFontMetrics().getAverageCharWidth();
180         mSmallFontHeight = gc.getFontMetrics().getHeight();
181 
182         image.dispose();
183         gc.dispose();
184 
185         setLayout(new FillLayout());
186 
187         // Create a sash form for holding two canvas views, one for the
188         // thread labels and one for the thread timeline.
189         mSashForm = new SashForm(this, SWT.HORIZONTAL);
190         mSashForm.setBackground(mColorGray);
191         mSashForm.SASH_WIDTH = 3;
192 
193         // Create a composite for the left side of the sash
194         Composite composite = new Composite(mSashForm, SWT.NONE);
195         GridLayout layout = new GridLayout(1, true /* make columns equal width */);
196         layout.marginHeight = 0;
197         layout.marginWidth = 0;
198         layout.verticalSpacing = 1;
199         composite.setLayout(layout);
200 
201         // Create a blank corner space in the upper left corner
202         BlankCorner corner = new BlankCorner(composite);
203         GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
204         gridData.heightHint = topMargin;
205         corner.setLayoutData(gridData);
206 
207         // Add the thread labels below the blank corner.
208         mLabels = new RowLabels(composite);
209         gridData = new GridData(GridData.FILL_BOTH);
210         mLabels.setLayoutData(gridData);
211 
212         // Create another composite for the right side of the sash
213         composite = new Composite(mSashForm, SWT.NONE);
214         layout = new GridLayout(1, true /* make columns equal width */);
215         layout.marginHeight = 0;
216         layout.marginWidth = 0;
217         layout.verticalSpacing = 1;
218         composite.setLayout(layout);
219 
220         mTimescale = new Timescale(composite);
221         gridData = new GridData(GridData.FILL_HORIZONTAL);
222         gridData.heightHint = topMargin;
223         mTimescale.setLayoutData(gridData);
224 
225         mSurface = new Surface(composite);
226         gridData = new GridData(GridData.FILL_BOTH);
227         mSurface.setLayoutData(gridData);
228         mSashForm.setWeights(new int[] { 1, 5 });
229 
230         final ScrollBar vBar = mSurface.getVerticalBar();
231         vBar.addListener(SWT.Selection, new Listener() {
232            @Override
233         public void handleEvent(Event e) {
234                mScrollOffsetY = vBar.getSelection();
235                Point dim = mSurface.getSize();
236                int newScrollOffsetY = computeVisibleRows(dim.y);
237                if (newScrollOffsetY != mScrollOffsetY) {
238                    mScrollOffsetY = newScrollOffsetY;
239                    vBar.setSelection(newScrollOffsetY);
240                }
241                mLabels.redraw();
242                mSurface.redraw();
243            }
244         });
245 
246         final ScrollBar hBar = mSurface.getHorizontalBar();
247         hBar.addListener(SWT.Selection, new Listener() {
248             @Override
249             public void handleEvent(Event e) {
250                 mSurface.setScaleFromHorizontalScrollBar(hBar.getSelection());
251                 mSurface.redraw();
252             }
253         });
254 
255         mSurface.addListener(SWT.Resize, new Listener() {
256             @Override
257             public void handleEvent(Event e) {
258                 Point dim = mSurface.getSize();
259 
260                 // If we don't need the scroll bar then don't display it.
261                 if (dim.y >= mNumRows * rowYSpace) {
262                     vBar.setVisible(false);
263                 } else {
264                     vBar.setVisible(true);
265                 }
266                 int newScrollOffsetY = computeVisibleRows(dim.y);
267                 if (newScrollOffsetY != mScrollOffsetY) {
268                     mScrollOffsetY = newScrollOffsetY;
269                     vBar.setSelection(newScrollOffsetY);
270                 }
271 
272                 int spaceNeeded = mNumRows * rowYSpace;
273                 vBar.setMaximum(spaceNeeded);
274                 vBar.setThumb(dim.y);
275 
276                 mLabels.redraw();
277                 mSurface.redraw();
278             }
279         });
280 
281         mSurface.addMouseListener(new MouseAdapter() {
282             @Override
283             public void mouseUp(MouseEvent me) {
284                 mSurface.mouseUp(me);
285             }
286 
287             @Override
288             public void mouseDown(MouseEvent me) {
289                 mSurface.mouseDown(me);
290             }
291 
292             @Override
293             public void mouseDoubleClick(MouseEvent me) {
294                 mSurface.mouseDoubleClick(me);
295             }
296         });
297 
298         mSurface.addMouseMoveListener(new MouseMoveListener() {
299             @Override
300             public void mouseMove(MouseEvent me) {
301                 mSurface.mouseMove(me);
302             }
303         });
304 
305         mSurface.addMouseWheelListener(new MouseWheelListener() {
306             @Override
307             public void mouseScrolled(MouseEvent me) {
308                 mSurface.mouseScrolled(me);
309             }
310         });
311 
312         mTimescale.addMouseListener(new MouseAdapter() {
313             @Override
314             public void mouseUp(MouseEvent me) {
315                 mTimescale.mouseUp(me);
316             }
317 
318             @Override
319             public void mouseDown(MouseEvent me) {
320                 mTimescale.mouseDown(me);
321             }
322 
323             @Override
324             public void mouseDoubleClick(MouseEvent me) {
325                 mTimescale.mouseDoubleClick(me);
326             }
327         });
328 
329         mTimescale.addMouseMoveListener(new MouseMoveListener() {
330             @Override
331             public void mouseMove(MouseEvent me) {
332                 mTimescale.mouseMove(me);
333             }
334         });
335 
336         mLabels.addMouseMoveListener(new MouseMoveListener() {
337             @Override
338             public void mouseMove(MouseEvent me) {
339                 mLabels.mouseMove(me);
340             }
341         });
342 
343         setData(reader.getThreadTimeRecords());
344     }
345 
346     @Override
update(Observable objservable, Object arg)347     public void update(Observable objservable, Object arg) {
348         // Ignore updates from myself
349         if (arg == "TimeLineView")  //$NON-NLS-1$
350             return;
351         // System.out.printf("timeline update from %s\n", arg);
352         boolean foundHighlight = false;
353         ArrayList<Selection> selections;
354         selections = mSelectionController.getSelections();
355         for (Selection selection : selections) {
356             Selection.Action action = selection.getAction();
357             if (action != Selection.Action.Highlight)
358                 continue;
359             String name = selection.getName();
360             // System.out.printf(" timeline highlight %s from %s\n", name, arg);
361             if (name == "MethodData") {  //$NON-NLS-1$
362                 foundHighlight = true;
363                 mHighlightMethodData = (MethodData) selection.getValue();
364                 // System.out.printf(" method %s\n",
365                 // highlightMethodData.getName());
366                 mHighlightCall = null;
367                 startHighlighting();
368             } else if (name == "Call") {  //$NON-NLS-1$
369                 foundHighlight = true;
370                 mHighlightCall = (Call) selection.getValue();
371                 // System.out.printf(" call %s\n", highlightCall.getName());
372                 mHighlightMethodData = null;
373                 startHighlighting();
374             }
375         }
376         if (foundHighlight == false)
377             mSurface.clearHighlights();
378     }
379 
setData(ArrayList<Record> records)380     public void setData(ArrayList<Record> records) {
381         if (records == null)
382             records = new ArrayList<Record>();
383 
384         if (false) {
385             System.out.println("TimelineView() list of records:");  //$NON-NLS-1$
386             for (Record r : records) {
387                 System.out.printf("row '%s' block '%s' [%d, %d]\n", r.row  //$NON-NLS-1$
388                         .getName(), r.block.getName(), r.block.getStartTime(),
389                         r.block.getEndTime());
390                 if (r.block.getStartTime() > r.block.getEndTime()) {
391                     System.err.printf("Error: block startTime > endTime\n");  //$NON-NLS-1$
392                     System.exit(1);
393                 }
394             }
395         }
396 
397         // Sort the records into increasing start time, and decreasing end time
398         Collections.sort(records, new Comparator<Record>() {
399             @Override
400             public int compare(Record r1, Record r2) {
401                 long start1 = r1.block.getStartTime();
402                 long start2 = r2.block.getStartTime();
403                 if (start1 > start2)
404                     return 1;
405                 if (start1 < start2)
406                     return -1;
407 
408                 // The start times are the same, so compare the end times
409                 long end1 = r1.block.getEndTime();
410                 long end2 = r2.block.getEndTime();
411                 if (end1 > end2)
412                     return -1;
413                 if (end1 < end2)
414                     return 1;
415 
416                 return 0;
417             }
418         });
419 
420         ArrayList<Segment> segmentList = new ArrayList<Segment>();
421 
422         // The records are sorted into increasing start time,
423         // so the minimum start time is the start time of the first record.
424         double minVal = 0;
425         if (records.size() > 0)
426             minVal = records.get(0).block.getStartTime();
427 
428         // Sum the time spent in each row and block, and
429         // keep track of the maximum end time.
430         double maxVal = 0;
431         for (Record rec : records) {
432             Row row = rec.row;
433             Block block = rec.block;
434             if (block.isIgnoredBlock()) {
435                 continue;
436             }
437 
438             String rowName = row.getName();
439             RowData rd = mRowByName.get(rowName);
440             if (rd == null) {
441                 rd = new RowData(row);
442                 mRowByName.put(rowName, rd);
443             }
444             long blockStartTime = block.getStartTime();
445             long blockEndTime = block.getEndTime();
446             if (blockEndTime > rd.mEndTime) {
447                 long start = Math.max(blockStartTime, rd.mEndTime);
448                 rd.mElapsed += blockEndTime - start;
449                 rd.mEndTime = blockEndTime;
450             }
451             if (blockEndTime > maxVal)
452                 maxVal = blockEndTime;
453 
454             // Keep track of nested blocks by using a stack (for each row).
455             // Create a Segment object for each visible part of a block.
456             Block top = rd.top();
457             if (top == null) {
458                 rd.push(block);
459                 continue;
460             }
461 
462             long topStartTime = top.getStartTime();
463             long topEndTime = top.getEndTime();
464             if (topEndTime >= blockStartTime) {
465                 // Add this segment if it has a non-zero elapsed time.
466                 if (topStartTime < blockStartTime) {
467                     Segment segment = new Segment(rd, top, topStartTime,
468                             blockStartTime);
469                     segmentList.add(segment);
470                 }
471 
472                 // If this block starts where the previous (top) block ends,
473                 // then pop off the top block.
474                 if (topEndTime == blockStartTime)
475                     rd.pop();
476                 rd.push(block);
477             } else {
478                 // We may have to pop several frames here.
479                 popFrames(rd, top, blockStartTime, segmentList);
480                 rd.push(block);
481             }
482         }
483 
484         // Clean up the stack of each row
485         for (RowData rd : mRowByName.values()) {
486             Block top = rd.top();
487             popFrames(rd, top, Integer.MAX_VALUE, segmentList);
488         }
489 
490         mSurface.setRange(minVal, maxVal);
491         mSurface.setLimitRange(minVal, maxVal);
492 
493         // Sort the rows into decreasing elapsed time
494         Collection<RowData> rv = mRowByName.values();
495         mRows = rv.toArray(new RowData[rv.size()]);
496         Arrays.sort(mRows, new Comparator<RowData>() {
497             @Override
498             public int compare(RowData rd1, RowData rd2) {
499                 return (int) (rd2.mElapsed - rd1.mElapsed);
500             }
501         });
502 
503         // Assign ranks to the sorted rows
504         for (int ii = 0; ii < mRows.length; ++ii) {
505             mRows[ii].mRank = ii;
506         }
507 
508         // Compute the number of rows with data
509         mNumRows = 0;
510         for (int ii = 0; ii < mRows.length; ++ii) {
511             if (mRows[ii].mElapsed == 0)
512                 break;
513             mNumRows += 1;
514         }
515 
516         // Sort the blocks into increasing rows, and within rows into
517         // increasing start values.
518         mSegments = segmentList.toArray(new Segment[segmentList.size()]);
519         Arrays.sort(mSegments, new Comparator<Segment>() {
520             @Override
521             public int compare(Segment bd1, Segment bd2) {
522                 RowData rd1 = bd1.mRowData;
523                 RowData rd2 = bd2.mRowData;
524                 int diff = rd1.mRank - rd2.mRank;
525                 if (diff == 0) {
526                     long timeDiff = bd1.mStartTime - bd2.mStartTime;
527                     if (timeDiff == 0)
528                         timeDiff = bd1.mEndTime - bd2.mEndTime;
529                     return (int) timeDiff;
530                 }
531                 return diff;
532             }
533         });
534 
535         if (false) {
536             for (Segment segment : mSegments) {
537                 System.out.printf("seg '%s' [%6d, %6d] %s\n",
538                         segment.mRowData.mName, segment.mStartTime,
539                         segment.mEndTime, segment.mBlock.getName());
540                 if (segment.mStartTime > segment.mEndTime) {
541                     System.err.printf("Error: segment startTime > endTime\n");
542                     System.exit(1);
543                 }
544             }
545         }
546     }
547 
popFrames(RowData rd, Block top, long startTime, ArrayList<Segment> segmentList)548     private static void popFrames(RowData rd, Block top, long startTime,
549             ArrayList<Segment> segmentList) {
550         long topEndTime = top.getEndTime();
551         long lastEndTime = top.getStartTime();
552         while (topEndTime <= startTime) {
553             if (topEndTime > lastEndTime) {
554                 Segment segment = new Segment(rd, top, lastEndTime, topEndTime);
555                 segmentList.add(segment);
556                 lastEndTime = topEndTime;
557             }
558             rd.pop();
559             top = rd.top();
560             if (top == null)
561                 return;
562             topEndTime = top.getEndTime();
563         }
564 
565         // If we get here, then topEndTime > startTime
566         if (lastEndTime < startTime) {
567             Segment bd = new Segment(rd, top, lastEndTime, startTime);
568             segmentList.add(bd);
569         }
570     }
571 
572     private class RowLabels extends Canvas {
573 
574         /** The space between the row label and the sash line */
575         private static final int labelMarginX = 2;
576 
RowLabels(Composite parent)577         public RowLabels(Composite parent) {
578             super(parent, SWT.NO_BACKGROUND);
579             addPaintListener(new PaintListener() {
580                 @Override
581                 public void paintControl(PaintEvent pe) {
582                     draw(pe.display, pe.gc);
583                 }
584             });
585         }
586 
mouseMove(MouseEvent me)587         private void mouseMove(MouseEvent me) {
588             int rownum = (me.y + mScrollOffsetY) / rowYSpace;
589             if (mMouseRow != rownum) {
590                 mMouseRow = rownum;
591                 redraw();
592                 mSurface.redraw();
593             }
594         }
595 
draw(Display display, GC gc)596         private void draw(Display display, GC gc) {
597             if (mSegments.length == 0) {
598                 // gc.setBackground(colorBackground);
599                 // gc.fillRectangle(getBounds());
600                 return;
601             }
602             Point dim = getSize();
603 
604             // Create an image for double-buffering
605             Image image = new Image(display, getBounds());
606 
607             // Set up the off-screen gc
608             GC gcImage = new GC(image);
609             if (mSetFonts)
610                 gcImage.setFont(mFontRegistry.get("medium"));  //$NON-NLS-1$
611 
612             if (mNumRows > 2) {
613                 // Draw the row background stripes
614                 gcImage.setBackground(mColorRowBack);
615                 for (int ii = 1; ii < mNumRows; ii += 2) {
616                     RowData rd = mRows[ii];
617                     int y1 = rd.mRank * rowYSpace - mScrollOffsetY;
618                     gcImage.fillRectangle(0, y1, dim.x, rowYSpace);
619                 }
620             }
621 
622             // Draw the row labels
623             int offsetY = rowYMarginHalf - mScrollOffsetY;
624             for (int ii = mStartRow; ii <= mEndRow; ++ii) {
625                 RowData rd = mRows[ii];
626                 int y1 = rd.mRank * rowYSpace + offsetY;
627                 Point extent = gcImage.stringExtent(rd.mName);
628                 int x1 = dim.x - extent.x - labelMarginX;
629                 gcImage.drawString(rd.mName, x1, y1, true);
630             }
631 
632             // Draw a highlight box on the row where the mouse is.
633             if (mMouseRow >= mStartRow && mMouseRow <= mEndRow) {
634                 gcImage.setForeground(mColorGray);
635                 int y1 = mMouseRow * rowYSpace - mScrollOffsetY;
636                 gcImage.drawRectangle(0, y1, dim.x, rowYSpace);
637             }
638 
639             // Draw the off-screen buffer to the screen
640             gc.drawImage(image, 0, 0);
641 
642             // Clean up
643             image.dispose();
644             gcImage.dispose();
645         }
646     }
647 
648     private class BlankCorner extends Canvas {
BlankCorner(Composite parent)649         public BlankCorner(Composite parent) {
650             //super(parent, SWT.NO_BACKGROUND);
651             super(parent, SWT.NONE);
652             addPaintListener(new PaintListener() {
653                 @Override
654                 public void paintControl(PaintEvent pe) {
655                     draw(pe.display, pe.gc);
656                 }
657             });
658         }
659 
draw(Display display, GC gc)660         private void draw(Display display, GC gc) {
661             // Create a blank image and draw it to the canvas
662             Image image = new Image(display, getBounds());
663             gc.drawImage(image, 0, 0);
664 
665             // Clean up
666             image.dispose();
667         }
668     }
669 
670     private class Timescale extends Canvas {
671         private Point mMouse = new Point(LeftMargin, 0);
672         private Cursor mZoomCursor;
673         private String mMethodName = null;
674         private Color mMethodColor = null;
675         private String mDetails;
676         private int mMethodStartY;
677         private int mDetailsStartY;
678         private int mMarkStartX;
679         private int mMarkEndX;
680 
681         /** The space between the colored block and the method name */
682         private static final int METHOD_BLOCK_MARGIN = 10;
683 
Timescale(Composite parent)684         public Timescale(Composite parent) {
685             //super(parent, SWT.NO_BACKGROUND);
686             super(parent, SWT.NONE);
687             Display display = getDisplay();
688             mZoomCursor = new Cursor(display, SWT.CURSOR_SIZEWE);
689             setCursor(mZoomCursor);
690             mMethodStartY = mSmallFontHeight + 1;
691             mDetailsStartY = mMethodStartY + mSmallFontHeight + 1;
692             addPaintListener(new PaintListener() {
693                 @Override
694                 public void paintControl(PaintEvent pe) {
695                     draw(pe.display, pe.gc);
696                 }
697             });
698         }
699 
setVbarPosition(int x)700         public void setVbarPosition(int x) {
701             mMouse.x = x;
702         }
703 
setMarkStart(int x)704         public void setMarkStart(int x) {
705             mMarkStartX = x;
706         }
707 
setMarkEnd(int x)708         public void setMarkEnd(int x) {
709             mMarkEndX = x;
710         }
711 
setMethodName(String name)712         public void setMethodName(String name) {
713             mMethodName = name;
714         }
715 
setMethodColor(Color color)716         public void setMethodColor(Color color) {
717             mMethodColor = color;
718         }
719 
setDetails(String details)720         public void setDetails(String details) {
721             mDetails = details;
722         }
723 
mouseMove(MouseEvent me)724         private void mouseMove(MouseEvent me) {
725             me.y = -1;
726             mSurface.mouseMove(me);
727         }
728 
mouseDown(MouseEvent me)729         private void mouseDown(MouseEvent me) {
730             mSurface.startScaling(me.x);
731             mSurface.redraw();
732         }
733 
mouseUp(MouseEvent me)734         private void mouseUp(MouseEvent me) {
735             mSurface.stopScaling(me.x);
736         }
737 
mouseDoubleClick(MouseEvent me)738         private void mouseDoubleClick(MouseEvent me) {
739             mSurface.resetScale();
740             mSurface.redraw();
741         }
742 
draw(Display display, GC gc)743         private void draw(Display display, GC gc) {
744             Point dim = getSize();
745 
746             // Create an image for double-buffering
747             Image image = new Image(display, getBounds());
748 
749             // Set up the off-screen gc
750             GC gcImage = new GC(image);
751             if (mSetFonts)
752                 gcImage.setFont(mFontRegistry.get("medium"));  //$NON-NLS-1$
753 
754             if (mSurface.drawingSelection()) {
755                 drawSelection(display, gcImage);
756             }
757 
758             drawTicks(display, gcImage);
759 
760             // Draw the vertical bar where the mouse is
761             gcImage.setForeground(mColorDarkGray);
762             gcImage.drawLine(mMouse.x, timeLineOffsetY, mMouse.x, dim.y);
763 
764             // Draw the current millseconds
765             drawTickLegend(display, gcImage);
766 
767             // Draw the method name and color, if needed
768             drawMethod(display, gcImage);
769 
770             // Draw the details, if needed
771             drawDetails(display, gcImage);
772 
773             // Draw the off-screen buffer to the screen
774             gc.drawImage(image, 0, 0);
775 
776             // Clean up
777             image.dispose();
778             gcImage.dispose();
779         }
780 
drawSelection(Display display, GC gc)781         private void drawSelection(Display display, GC gc) {
782             Point dim = getSize();
783             gc.setForeground(mColorGray);
784             gc.drawLine(mMarkStartX, timeLineOffsetY, mMarkStartX, dim.y);
785             gc.setBackground(mColorZoomSelection);
786             int x, width;
787             if (mMarkStartX < mMarkEndX) {
788                 x = mMarkStartX;
789                 width = mMarkEndX - mMarkStartX;
790             } else {
791                 x = mMarkEndX;
792                 width = mMarkStartX - mMarkEndX;
793             }
794             if (width > 1) {
795                 gc.fillRectangle(x, timeLineOffsetY, width, dim.y);
796             }
797         }
798 
drawTickLegend(Display display, GC gc)799         private void drawTickLegend(Display display, GC gc) {
800             int mouseX = mMouse.x - LeftMargin;
801             double mouseXval = mScaleInfo.pixelToValue(mouseX);
802             String info = mUnits.labelledString(mouseXval);
803             gc.setForeground(mColorForeground);
804             gc.drawString(info, LeftMargin + 2, 1, true);
805 
806             // Display the maximum data value
807             double maxVal = mScaleInfo.getMaxVal();
808             info = mUnits.labelledString(maxVal);
809             if (mClockSource != null) {
810                 info = String.format(" max %s (%s)", info, mClockSource);  //$NON-NLS-1$
811             } else {
812                 info = String.format(" max %s ", info);  //$NON-NLS-1$
813             }
814             Point extent = gc.stringExtent(info);
815             Point dim = getSize();
816             int x1 = dim.x - RightMargin - extent.x;
817             gc.drawString(info, x1, 1, true);
818         }
819 
drawMethod(Display display, GC gc)820         private void drawMethod(Display display, GC gc) {
821             if (mMethodName == null) {
822                 return;
823             }
824 
825             int x1 = LeftMargin;
826             int y1 = mMethodStartY;
827             gc.setBackground(mMethodColor);
828             int width = 2 * mSmallFontWidth;
829             gc.fillRectangle(x1, y1, width, mSmallFontHeight);
830             x1 += width + METHOD_BLOCK_MARGIN;
831             gc.drawString(mMethodName, x1, y1, true);
832         }
833 
drawDetails(Display display, GC gc)834         private void drawDetails(Display display, GC gc) {
835             if (mDetails == null) {
836                 return;
837             }
838 
839             int x1 = LeftMargin + 2 * mSmallFontWidth + METHOD_BLOCK_MARGIN;
840             int y1 = mDetailsStartY;
841             gc.drawString(mDetails, x1, y1, true);
842         }
843 
drawTicks(Display display, GC gc)844         private void drawTicks(Display display, GC gc) {
845             Point dim = getSize();
846             int y2 = majorTickLength + timeLineOffsetY;
847             int y3 = minorTickLength + timeLineOffsetY;
848             int y4 = y2 + tickToFontSpacing;
849             gc.setForeground(mColorForeground);
850             gc.drawLine(LeftMargin, timeLineOffsetY, dim.x - RightMargin,
851                     timeLineOffsetY);
852             double minVal = mScaleInfo.getMinVal();
853             double maxVal = mScaleInfo.getMaxVal();
854             double minMajorTick = mScaleInfo.getMinMajorTick();
855             double tickIncrement = mScaleInfo.getTickIncrement();
856             double minorTickIncrement = tickIncrement / 5;
857             double pixelsPerRange = mScaleInfo.getPixelsPerRange();
858 
859             // Draw the initial minor ticks, if any
860             if (minVal < minMajorTick) {
861                 gc.setForeground(mColorGray);
862                 double xMinor = minMajorTick;
863                 for (int ii = 1; ii <= 4; ++ii) {
864                     xMinor -= minorTickIncrement;
865                     if (xMinor < minVal)
866                         break;
867                     int x1 = LeftMargin
868                             + (int) (0.5 + (xMinor - minVal) * pixelsPerRange);
869                     gc.drawLine(x1, timeLineOffsetY, x1, y3);
870                 }
871             }
872 
873             if (tickIncrement <= 10) {
874                 // TODO avoid rendering the loop when tickIncrement is invalid. It can be zero
875                 // or too small.
876                 // System.out.println(String.format("Timescale.drawTicks error: tickIncrement=%1f", tickIncrement));
877                 return;
878             }
879             for (double x = minMajorTick; x <= maxVal; x += tickIncrement) {
880                 int x1 = LeftMargin
881                         + (int) (0.5 + (x - minVal) * pixelsPerRange);
882 
883                 // Draw a major tick
884                 gc.setForeground(mColorForeground);
885                 gc.drawLine(x1, timeLineOffsetY, x1, y2);
886                 if (x > maxVal)
887                     break;
888 
889                 // Draw the tick text
890                 String tickString = mUnits.valueOf(x);
891                 gc.drawString(tickString, x1, y4, true);
892 
893                 // Draw 4 minor ticks between major ticks
894                 gc.setForeground(mColorGray);
895                 double xMinor = x;
896                 for (int ii = 1; ii <= 4; ii++) {
897                     xMinor += minorTickIncrement;
898                     if (xMinor > maxVal)
899                         break;
900                     x1 = LeftMargin
901                             + (int) (0.5 + (xMinor - minVal) * pixelsPerRange);
902                     gc.drawLine(x1, timeLineOffsetY, x1, y3);
903                 }
904             }
905         }
906     }
907 
908     private static enum GraphicsState {
909         Normal, Marking, Scaling, Animating, Scrolling
910     };
911 
912     private class Surface extends Canvas {
913 
Surface(Composite parent)914         public Surface(Composite parent) {
915             super(parent, SWT.NO_BACKGROUND | SWT.V_SCROLL | SWT.H_SCROLL);
916             Display display = getDisplay();
917             mNormalCursor = new Cursor(display, SWT.CURSOR_CROSS);
918             mIncreasingCursor = new Cursor(display, SWT.CURSOR_SIZEE);
919             mDecreasingCursor = new Cursor(display, SWT.CURSOR_SIZEW);
920 
921             initZoomFractionsWithExp();
922 
923             addPaintListener(new PaintListener() {
924                 @Override
925                 public void paintControl(PaintEvent pe) {
926                     draw(pe.display, pe.gc);
927                 }
928             });
929 
930             mZoomAnimator = new Runnable() {
931                 @Override
932                 public void run() {
933                     animateZoom();
934                 }
935             };
936 
937             mHighlightAnimator = new Runnable() {
938                 @Override
939                 public void run() {
940                     animateHighlight();
941                 }
942             };
943         }
944 
initZoomFractionsWithExp()945         private void initZoomFractionsWithExp() {
946             mZoomFractions = new double[ZOOM_STEPS];
947             int next = 0;
948             for (int ii = 0; ii < ZOOM_STEPS / 2; ++ii, ++next) {
949                 mZoomFractions[next] = (double) (1 << ii)
950                         / (double) (1 << (ZOOM_STEPS / 2));
951                 // System.out.printf("%d %f\n", next, zoomFractions[next]);
952             }
953             for (int ii = 2; ii < 2 + ZOOM_STEPS / 2; ++ii, ++next) {
954                 mZoomFractions[next] = (double) ((1 << ii) - 1)
955                         / (double) (1 << ii);
956                 // System.out.printf("%d %f\n", next, zoomFractions[next]);
957             }
958         }
959 
960         @SuppressWarnings("unused")
initZoomFractionsWithSinWave()961         private void initZoomFractionsWithSinWave() {
962             mZoomFractions = new double[ZOOM_STEPS];
963             for (int ii = 0; ii < ZOOM_STEPS; ++ii) {
964                 double offset = Math.PI * ii / ZOOM_STEPS;
965                 mZoomFractions[ii] = (Math.sin((1.5 * Math.PI + offset)) + 1.0) / 2.0;
966                 // System.out.printf("%d %f\n", ii, zoomFractions[ii]);
967             }
968         }
969 
setRange(double minVal, double maxVal)970         public void setRange(double minVal, double maxVal) {
971             mMinDataVal = minVal;
972             mMaxDataVal = maxVal;
973             mScaleInfo.setMinVal(minVal);
974             mScaleInfo.setMaxVal(maxVal);
975         }
976 
setLimitRange(double minVal, double maxVal)977         public void setLimitRange(double minVal, double maxVal) {
978             mLimitMinVal = minVal;
979             mLimitMaxVal = maxVal;
980         }
981 
resetScale()982         public void resetScale() {
983             mScaleInfo.setMinVal(mLimitMinVal);
984             mScaleInfo.setMaxVal(mLimitMaxVal);
985         }
986 
setScaleFromHorizontalScrollBar(int selection)987         public void setScaleFromHorizontalScrollBar(int selection) {
988             double minVal = mScaleInfo.getMinVal();
989             double maxVal = mScaleInfo.getMaxVal();
990             double visibleRange = maxVal - minVal;
991 
992             minVal = mLimitMinVal + selection;
993             maxVal = minVal + visibleRange;
994             if (maxVal > mLimitMaxVal) {
995                 maxVal = mLimitMaxVal;
996                 minVal = maxVal - visibleRange;
997             }
998             mScaleInfo.setMinVal(minVal);
999             mScaleInfo.setMaxVal(maxVal);
1000 
1001             mGraphicsState = GraphicsState.Scrolling;
1002         }
1003 
updateHorizontalScrollBar()1004         private void updateHorizontalScrollBar() {
1005             double minVal = mScaleInfo.getMinVal();
1006             double maxVal = mScaleInfo.getMaxVal();
1007             double visibleRange = maxVal - minVal;
1008             double fullRange = mLimitMaxVal - mLimitMinVal;
1009 
1010             ScrollBar hBar = getHorizontalBar();
1011             if (fullRange > visibleRange) {
1012                 hBar.setVisible(true);
1013                 hBar.setMinimum(0);
1014                 hBar.setMaximum((int)Math.ceil(fullRange));
1015                 hBar.setThumb((int)Math.ceil(visibleRange));
1016                 hBar.setSelection((int)Math.floor(minVal - mLimitMinVal));
1017             } else {
1018                 hBar.setVisible(false);
1019             }
1020         }
1021 
draw(Display display, GC gc)1022         private void draw(Display display, GC gc) {
1023             if (mSegments.length == 0) {
1024                 // gc.setBackground(colorBackground);
1025                 // gc.fillRectangle(getBounds());
1026                 return;
1027             }
1028 
1029             // Create an image for double-buffering
1030             Image image = new Image(display, getBounds());
1031 
1032             // Set up the off-screen gc
1033             GC gcImage = new GC(image);
1034             if (mSetFonts)
1035                 gcImage.setFont(mFontRegistry.get("small"));  //$NON-NLS-1$
1036 
1037             // Draw the background
1038             // gcImage.setBackground(colorBackground);
1039             // gcImage.fillRectangle(image.getBounds());
1040 
1041             if (mGraphicsState == GraphicsState.Scaling) {
1042                 double diff = mMouse.x - mMouseMarkStartX;
1043                 if (diff > 0) {
1044                     double newMinVal = mScaleMinVal - diff / mScalePixelsPerRange;
1045                     if (newMinVal < mLimitMinVal)
1046                         newMinVal = mLimitMinVal;
1047                     mScaleInfo.setMinVal(newMinVal);
1048                     // System.out.printf("diff %f scaleMin %f newMin %f\n",
1049                     // diff, scaleMinVal, newMinVal);
1050                 } else if (diff < 0) {
1051                     double newMaxVal = mScaleMaxVal - diff / mScalePixelsPerRange;
1052                     if (newMaxVal > mLimitMaxVal)
1053                         newMaxVal = mLimitMaxVal;
1054                     mScaleInfo.setMaxVal(newMaxVal);
1055                     // System.out.printf("diff %f scaleMax %f newMax %f\n",
1056                     // diff, scaleMaxVal, newMaxVal);
1057                 }
1058             }
1059 
1060             // Recompute the ticks and strips only if the size has changed,
1061             // or we scrolled so that a new row is visible.
1062             Point dim = getSize();
1063             if (mStartRow != mCachedStartRow || mEndRow != mCachedEndRow
1064                     || mScaleInfo.getMinVal() != mCachedMinVal
1065                     || mScaleInfo.getMaxVal() != mCachedMaxVal) {
1066                 mCachedStartRow = mStartRow;
1067                 mCachedEndRow = mEndRow;
1068                 int xdim = dim.x - TotalXMargin;
1069                 mScaleInfo.setNumPixels(xdim);
1070                 boolean forceEndPoints = (mGraphicsState == GraphicsState.Scaling
1071                         || mGraphicsState == GraphicsState.Animating
1072                         || mGraphicsState == GraphicsState.Scrolling);
1073                 mScaleInfo.computeTicks(forceEndPoints);
1074                 mCachedMinVal = mScaleInfo.getMinVal();
1075                 mCachedMaxVal = mScaleInfo.getMaxVal();
1076                 if (mLimitMinVal > mScaleInfo.getMinVal())
1077                     mLimitMinVal = mScaleInfo.getMinVal();
1078                 if (mLimitMaxVal < mScaleInfo.getMaxVal())
1079                     mLimitMaxVal = mScaleInfo.getMaxVal();
1080 
1081                 // Compute the strips
1082                 computeStrips();
1083 
1084                 // Update the horizontal scrollbar.
1085                 updateHorizontalScrollBar();
1086             }
1087 
1088             if (mNumRows > 2) {
1089                 // Draw the row background stripes
1090                 gcImage.setBackground(mColorRowBack);
1091                 for (int ii = 1; ii < mNumRows; ii += 2) {
1092                     RowData rd = mRows[ii];
1093                     int y1 = rd.mRank * rowYSpace - mScrollOffsetY;
1094                     gcImage.fillRectangle(0, y1, dim.x, rowYSpace);
1095                 }
1096             }
1097 
1098             if (drawingSelection()) {
1099                 drawSelection(display, gcImage);
1100             }
1101 
1102             String blockName = null;
1103             Color blockColor = null;
1104             String blockDetails = null;
1105 
1106             if (mDebug) {
1107                 double pixelsPerRange = mScaleInfo.getPixelsPerRange();
1108                 System.out
1109                         .printf(
1110                                 "dim.x %d pixels %d minVal %f, maxVal %f ppr %f rpp %f\n",
1111                                 dim.x, dim.x - TotalXMargin, mScaleInfo
1112                                         .getMinVal(), mScaleInfo.getMaxVal(),
1113                                 pixelsPerRange, 1.0 / pixelsPerRange);
1114             }
1115 
1116             // Draw the strips
1117             Block selectBlock = null;
1118             for (Strip strip : mStripList) {
1119                 if (strip.mColor == null) {
1120                     // System.out.printf("strip.color is null\n");
1121                     continue;
1122                 }
1123                 gcImage.setBackground(strip.mColor);
1124                 gcImage.fillRectangle(strip.mX, strip.mY - mScrollOffsetY, strip.mWidth,
1125                         strip.mHeight);
1126                 if (mMouseRow == strip.mRowData.mRank) {
1127                     if (mMouse.x >= strip.mX
1128                             && mMouse.x < strip.mX + strip.mWidth) {
1129                         Block block = strip.mSegment.mBlock;
1130                         blockName = block.getName();
1131                         blockColor = strip.mColor;
1132                         if (mHaveCpuTime) {
1133                             if (mHaveRealTime) {
1134                                 blockDetails = String.format(
1135                                         "excl cpu %s, incl cpu %s, "
1136                                         + "excl real %s, incl real %s",
1137                                         mUnits.labelledString(block.getExclusiveCpuTime()),
1138                                         mUnits.labelledString(block.getInclusiveCpuTime()),
1139                                         mUnits.labelledString(block.getExclusiveRealTime()),
1140                                         mUnits.labelledString(block.getInclusiveRealTime()));
1141                             } else {
1142                                 blockDetails = String.format(
1143                                         "excl cpu %s, incl cpu %s",
1144                                         mUnits.labelledString(block.getExclusiveCpuTime()),
1145                                         mUnits.labelledString(block.getInclusiveCpuTime()));
1146                             }
1147                         } else {
1148                             blockDetails = String.format(
1149                                     "excl real %s, incl real %s",
1150                                     mUnits.labelledString(block.getExclusiveRealTime()),
1151                                     mUnits.labelledString(block.getInclusiveRealTime()));
1152                         }
1153                     }
1154                     if (mMouseSelect.x >= strip.mX
1155                             && mMouseSelect.x < strip.mX + strip.mWidth) {
1156                         selectBlock = strip.mSegment.mBlock;
1157                     }
1158                 }
1159             }
1160             mMouseSelect.x = 0;
1161             mMouseSelect.y = 0;
1162 
1163             if (selectBlock != null) {
1164                 ArrayList<Selection> selections = new ArrayList<Selection>();
1165                 // Get the row label
1166                 RowData rd = mRows[mMouseRow];
1167                 selections.add(Selection.highlight("Thread", rd.mName));  //$NON-NLS-1$
1168                 selections.add(Selection.highlight("Call", selectBlock));  //$NON-NLS-1$
1169 
1170                 int mouseX = mMouse.x - LeftMargin;
1171                 double mouseXval = mScaleInfo.pixelToValue(mouseX);
1172                 selections.add(Selection.highlight("Time", mouseXval));  //$NON-NLS-1$
1173 
1174                 mSelectionController.change(selections, "TimeLineView");  //$NON-NLS-1$
1175                 mHighlightMethodData = null;
1176                 mHighlightCall = (Call) selectBlock;
1177                 startHighlighting();
1178             }
1179 
1180             // Draw a highlight box on the row where the mouse is.
1181             // Except don't draw the box if we are animating the
1182             // highlighing of a call or method because the inclusive
1183             // highlight bar passes through the highlight box and
1184             // causes an annoying flashing artifact.
1185             if (mMouseRow >= 0 && mMouseRow < mNumRows && mHighlightStep == 0) {
1186                 gcImage.setForeground(mColorGray);
1187                 int y1 = mMouseRow * rowYSpace - mScrollOffsetY;
1188                 gcImage.drawLine(0, y1, dim.x, y1);
1189                 gcImage.drawLine(0, y1 + rowYSpace, dim.x, y1 + rowYSpace);
1190             }
1191 
1192             // Highlight a selected method, if any
1193             drawHighlights(gcImage, dim);
1194 
1195             // Draw a vertical line where the mouse is.
1196             gcImage.setForeground(mColorDarkGray);
1197             int lineEnd = Math.min(dim.y, mNumRows * rowYSpace);
1198             gcImage.drawLine(mMouse.x, 0, mMouse.x, lineEnd);
1199 
1200             if (blockName != null) {
1201                 mTimescale.setMethodName(blockName);
1202                 mTimescale.setMethodColor(blockColor);
1203                 mTimescale.setDetails(blockDetails);
1204                 mShowHighlightName = false;
1205             } else if (mShowHighlightName) {
1206                 // Draw the highlighted method name
1207                 MethodData md = mHighlightMethodData;
1208                 if (md == null && mHighlightCall != null)
1209                     md = mHighlightCall.getMethodData();
1210                 if (md == null)
1211                     System.out.printf("null highlight?\n");  //$NON-NLS-1$
1212                 if (md != null) {
1213                     mTimescale.setMethodName(md.getProfileName());
1214                     mTimescale.setMethodColor(md.getColor());
1215                     mTimescale.setDetails(null);
1216                 }
1217             } else {
1218                 mTimescale.setMethodName(null);
1219                 mTimescale.setMethodColor(null);
1220                 mTimescale.setDetails(null);
1221             }
1222             mTimescale.redraw();
1223 
1224             // Draw the off-screen buffer to the screen
1225             gc.drawImage(image, 0, 0);
1226 
1227             // Clean up
1228             image.dispose();
1229             gcImage.dispose();
1230         }
1231 
drawHighlights(GC gc, Point dim)1232         private void drawHighlights(GC gc, Point dim) {
1233             int height = mHighlightHeight;
1234             if (height <= 0)
1235                 return;
1236             for (Range range : mHighlightExclusive) {
1237                 gc.setBackground(range.mColor);
1238                 int xStart = range.mXdim.x;
1239                 int width = range.mXdim.y;
1240                 gc.fillRectangle(xStart, range.mY - height - mScrollOffsetY, width, height);
1241             }
1242 
1243             // Draw the inclusive lines a bit shorter
1244             height -= 1;
1245             if (height <= 0)
1246                 height = 1;
1247 
1248             // Highlight the inclusive ranges
1249             gc.setForeground(mColorDarkGray);
1250             gc.setBackground(mColorDarkGray);
1251             for (Range range : mHighlightInclusive) {
1252                 int x1 = range.mXdim.x;
1253                 int x2 = range.mXdim.y;
1254                 boolean drawLeftEnd = false;
1255                 boolean drawRightEnd = false;
1256                 if (x1 >= LeftMargin)
1257                     drawLeftEnd = true;
1258                 else
1259                     x1 = LeftMargin;
1260                 if (x2 >= LeftMargin)
1261                     drawRightEnd = true;
1262                 else
1263                     x2 = dim.x - RightMargin;
1264                 int y1 = range.mY + rowHeight + 2 - mScrollOffsetY;
1265 
1266                 // If the range is very narrow, then just draw a small
1267                 // rectangle.
1268                 if (x2 - x1 < MinInclusiveRange) {
1269                     int width = x2 - x1;
1270                     if (width < 2)
1271                         width = 2;
1272                     gc.fillRectangle(x1, y1, width, height);
1273                     continue;
1274                 }
1275                 if (drawLeftEnd) {
1276                     if (drawRightEnd) {
1277                         // Draw both ends
1278                         int[] points = { x1, y1, x1, y1 + height, x2,
1279                                 y1 + height, x2, y1 };
1280                         gc.drawPolyline(points);
1281                     } else {
1282                         // Draw the left end
1283                         int[] points = { x1, y1, x1, y1 + height, x2,
1284                                 y1 + height };
1285                         gc.drawPolyline(points);
1286                     }
1287                 } else {
1288                     if (drawRightEnd) {
1289                         // Draw the right end
1290                         int[] points = { x1, y1 + height, x2, y1 + height, x2,
1291                                 y1 };
1292                         gc.drawPolyline(points);
1293                     } else {
1294                         // Draw neither end, just the line
1295                         int[] points = { x1, y1 + height, x2, y1 + height };
1296                         gc.drawPolyline(points);
1297                     }
1298                 }
1299 
1300                 // Draw the arrowheads, if necessary
1301                 if (drawLeftEnd == false) {
1302                     int[] points = { x1 + 7, y1 + height - 4, x1, y1 + height,
1303                             x1 + 7, y1 + height + 4 };
1304                     gc.fillPolygon(points);
1305                 }
1306                 if (drawRightEnd == false) {
1307                     int[] points = { x2 - 7, y1 + height - 4, x2, y1 + height,
1308                             x2 - 7, y1 + height + 4 };
1309                     gc.fillPolygon(points);
1310                 }
1311             }
1312         }
1313 
drawingSelection()1314         private boolean drawingSelection() {
1315             return mGraphicsState == GraphicsState.Marking
1316                     || mGraphicsState == GraphicsState.Animating;
1317         }
1318 
drawSelection(Display display, GC gc)1319         private void drawSelection(Display display, GC gc) {
1320             Point dim = getSize();
1321             gc.setForeground(mColorGray);
1322             gc.drawLine(mMouseMarkStartX, 0, mMouseMarkStartX, dim.y);
1323             gc.setBackground(mColorZoomSelection);
1324             int width;
1325             int mouseX = (mGraphicsState == GraphicsState.Animating) ? mMouseMarkEndX : mMouse.x;
1326             int x;
1327             if (mMouseMarkStartX < mouseX) {
1328                 x = mMouseMarkStartX;
1329                 width = mouseX - mMouseMarkStartX;
1330             } else {
1331                 x = mouseX;
1332                 width = mMouseMarkStartX - mouseX;
1333             }
1334             gc.fillRectangle(x, 0, width, dim.y);
1335         }
1336 
computeStrips()1337         private void computeStrips() {
1338             double minVal = mScaleInfo.getMinVal();
1339             double maxVal = mScaleInfo.getMaxVal();
1340 
1341             // Allocate space for the pixel data
1342             Pixel[] pixels = new Pixel[mNumRows];
1343             for (int ii = 0; ii < mNumRows; ++ii)
1344                 pixels[ii] = new Pixel();
1345 
1346             // Clear the per-block pixel data
1347             for (int ii = 0; ii < mSegments.length; ++ii) {
1348                 mSegments[ii].mBlock.clearWeight();
1349             }
1350 
1351             mStripList.clear();
1352             mHighlightExclusive.clear();
1353             mHighlightInclusive.clear();
1354             MethodData callMethod = null;
1355             long callStart = 0;
1356             long callEnd = -1;
1357             RowData callRowData = null;
1358             int prevMethodStart = -1;
1359             int prevMethodEnd = -1;
1360             int prevCallStart = -1;
1361             int prevCallEnd = -1;
1362             if (mHighlightCall != null) {
1363                 int callPixelStart = -1;
1364                 int callPixelEnd = -1;
1365                 callStart = mHighlightCall.getStartTime();
1366                 callEnd = mHighlightCall.getEndTime();
1367                 callMethod = mHighlightCall.getMethodData();
1368                 if (callStart >= minVal)
1369                     callPixelStart = mScaleInfo.valueToPixel(callStart);
1370                 if (callEnd <= maxVal)
1371                     callPixelEnd = mScaleInfo.valueToPixel(callEnd);
1372                 // System.out.printf("callStart,End %d,%d minVal,maxVal %f,%f
1373                 // callPixelStart,End %d,%d\n",
1374                 // callStart, callEnd, minVal, maxVal, callPixelStart,
1375                 // callPixelEnd);
1376                 int threadId = mHighlightCall.getThreadId();
1377                 String threadName = mThreadLabels.get(threadId);
1378                 callRowData = mRowByName.get(threadName);
1379                 int y1 = callRowData.mRank * rowYSpace + rowYMarginHalf;
1380                 Color color = callMethod.getColor();
1381                 mHighlightInclusive.add(new Range(callPixelStart + LeftMargin,
1382                         callPixelEnd + LeftMargin, y1, color));
1383             }
1384             for (Segment segment : mSegments) {
1385                 if (segment.mEndTime <= minVal)
1386                     continue;
1387                 if (segment.mStartTime >= maxVal)
1388                     continue;
1389 
1390                 Block block = segment.mBlock;
1391 
1392                 // Skip over blocks that were not assigned a color, including the
1393                 // top level block and others that have zero inclusive time.
1394                 Color color = block.getColor();
1395                 if (color == null)
1396                     continue;
1397 
1398                 double recordStart = Math.max(segment.mStartTime, minVal);
1399                 double recordEnd = Math.min(segment.mEndTime, maxVal);
1400                 if (recordStart == recordEnd)
1401                     continue;
1402                 int pixelStart = mScaleInfo.valueToPixel(recordStart);
1403                 int pixelEnd = mScaleInfo.valueToPixel(recordEnd);
1404                 int width = pixelEnd - pixelStart;
1405                 boolean isContextSwitch = segment.mIsContextSwitch;
1406 
1407                 RowData rd = segment.mRowData;
1408                 MethodData md = block.getMethodData();
1409 
1410                 // We will add the scroll offset later when we draw the strips
1411                 int y1 = rd.mRank * rowYSpace + rowYMarginHalf;
1412 
1413                 // If we can't display any more rows, then quit
1414                 if (rd.mRank > mEndRow)
1415                     break;
1416 
1417                 // System.out.printf("segment %s val: [%.1f, %.1f] frac [%f, %f]
1418                 // pixel: [%d, %d] pix.start %d weight %.2f %s\n",
1419                 // block.getName(), recordStart, recordEnd,
1420                 // scaleInfo.valueToPixelFraction(recordStart),
1421                 // scaleInfo.valueToPixelFraction(recordEnd),
1422                 // pixelStart, pixelEnd, pixels[rd.rank].start,
1423                 // pixels[rd.rank].maxWeight,
1424                 // pixels[rd.rank].segment != null
1425                 // ? pixels[rd.rank].segment.block.getName()
1426                 // : "null");
1427 
1428                 if (mHighlightMethodData != null) {
1429                     if (mHighlightMethodData == md) {
1430                         if (prevMethodStart != pixelStart || prevMethodEnd != pixelEnd) {
1431                             prevMethodStart = pixelStart;
1432                             prevMethodEnd = pixelEnd;
1433                             int rangeWidth = width;
1434                             if (rangeWidth == 0)
1435                                 rangeWidth = 1;
1436                             mHighlightExclusive.add(new Range(pixelStart
1437                                     + LeftMargin, rangeWidth, y1, color));
1438                             callStart = block.getStartTime();
1439                             int callPixelStart = -1;
1440                             if (callStart >= minVal)
1441                                 callPixelStart = mScaleInfo.valueToPixel(callStart);
1442                             int callPixelEnd = -1;
1443                             callEnd = block.getEndTime();
1444                             if (callEnd <= maxVal)
1445                                 callPixelEnd = mScaleInfo.valueToPixel(callEnd);
1446                             if (prevCallStart != callPixelStart || prevCallEnd != callPixelEnd) {
1447                                 prevCallStart = callPixelStart;
1448                                 prevCallEnd = callPixelEnd;
1449                                 mHighlightInclusive.add(new Range(
1450                                         callPixelStart + LeftMargin,
1451                                         callPixelEnd + LeftMargin, y1, color));
1452                             }
1453                         }
1454                     } else if (mFadeColors) {
1455                         color = md.getFadedColor();
1456                     }
1457                 } else if (mHighlightCall != null) {
1458                     if (segment.mStartTime >= callStart
1459                             && segment.mEndTime <= callEnd && callMethod == md
1460                             && callRowData == rd) {
1461                         if (prevMethodStart != pixelStart || prevMethodEnd != pixelEnd) {
1462                             prevMethodStart = pixelStart;
1463                             prevMethodEnd = pixelEnd;
1464                             int rangeWidth = width;
1465                             if (rangeWidth == 0)
1466                                 rangeWidth = 1;
1467                             mHighlightExclusive.add(new Range(pixelStart
1468                                     + LeftMargin, rangeWidth, y1, color));
1469                         }
1470                     } else if (mFadeColors) {
1471                         color = md.getFadedColor();
1472                     }
1473                 }
1474 
1475                 // Cases:
1476                 // 1. This segment starts on a different pixel than the
1477                 // previous segment started on. In this case, emit
1478                 // the pixel strip, if any, and:
1479                 // A. If the width is 0, then add this segment's
1480                 // weight to the Pixel.
1481                 // B. If the width > 0, then emit a strip for this
1482                 // segment (no partial Pixel data).
1483                 //
1484                 // 2. Otherwise (the new segment starts on the same
1485                 // pixel as the previous segment): add its "weight"
1486                 // to the current pixel, and:
1487                 // A. If the new segment has width 1,
1488                 // then emit the pixel strip and then
1489                 // add the segment's weight to the pixel.
1490                 // B. If the new segment has width > 1,
1491                 // then emit the pixel strip, and emit the rest
1492                 // of the strip for this segment (no partial Pixel
1493                 // data).
1494 
1495                 Pixel pix = pixels[rd.mRank];
1496                 if (pix.mStart != pixelStart) {
1497                     if (pix.mSegment != null) {
1498                         // Emit the pixel strip. This also clears the pixel.
1499                         emitPixelStrip(rd, y1, pix);
1500                     }
1501 
1502                     if (width == 0) {
1503                         // Compute the "weight" of this segment for the first
1504                         // pixel. For a pixel N, the "weight" of a segment is
1505                         // how much of the region [N - 0.5, N + 0.5] is covered
1506                         // by the segment.
1507                         double weight = computeWeight(recordStart, recordEnd,
1508                                 isContextSwitch, pixelStart);
1509                         weight = block.addWeight(pixelStart, rd.mRank, weight);
1510                         if (weight > pix.mMaxWeight) {
1511                             pix.setFields(pixelStart, weight, segment, color,
1512                                     rd);
1513                         }
1514                     } else {
1515                         int x1 = pixelStart + LeftMargin;
1516                         Strip strip = new Strip(
1517                                 x1, isContextSwitch ? y1 + rowHeight - 1 : y1,
1518                                 width, isContextSwitch ? 1 : rowHeight,
1519                                 rd, segment, color);
1520                         mStripList.add(strip);
1521                     }
1522                 } else {
1523                     double weight = computeWeight(recordStart, recordEnd,
1524                             isContextSwitch, pixelStart);
1525                     weight = block.addWeight(pixelStart, rd.mRank, weight);
1526                     if (weight > pix.mMaxWeight) {
1527                         pix.setFields(pixelStart, weight, segment, color, rd);
1528                     }
1529                     if (width == 1) {
1530                         // Emit the pixel strip. This also clears the pixel.
1531                         emitPixelStrip(rd, y1, pix);
1532 
1533                         // Compute the weight for the next pixel
1534                         pixelStart += 1;
1535                         weight = computeWeight(recordStart, recordEnd,
1536                                 isContextSwitch, pixelStart);
1537                         weight = block.addWeight(pixelStart, rd.mRank, weight);
1538                         pix.setFields(pixelStart, weight, segment, color, rd);
1539                     } else if (width > 1) {
1540                         // Emit the pixel strip. This also clears the pixel.
1541                         emitPixelStrip(rd, y1, pix);
1542 
1543                         // Emit a strip for the rest of the segment.
1544                         pixelStart += 1;
1545                         width -= 1;
1546                         int x1 = pixelStart + LeftMargin;
1547                         Strip strip = new Strip(
1548                                 x1, isContextSwitch ? y1 + rowHeight - 1 : y1,
1549                                 width, isContextSwitch ? 1 : rowHeight,
1550                                 rd,segment, color);
1551                         mStripList.add(strip);
1552                     }
1553                 }
1554             }
1555 
1556             // Emit the last pixels of each row, if any
1557             for (int ii = 0; ii < mNumRows; ++ii) {
1558                 Pixel pix = pixels[ii];
1559                 if (pix.mSegment != null) {
1560                     RowData rd = pix.mRowData;
1561                     int y1 = rd.mRank * rowYSpace + rowYMarginHalf;
1562                     // Emit the pixel strip. This also clears the pixel.
1563                     emitPixelStrip(rd, y1, pix);
1564                 }
1565             }
1566 
1567             if (false) {
1568                 System.out.printf("computeStrips()\n");
1569                 for (Strip strip : mStripList) {
1570                     System.out.printf("%3d, %3d width %3d height %d %s\n",
1571                             strip.mX, strip.mY, strip.mWidth, strip.mHeight,
1572                             strip.mSegment.mBlock.getName());
1573                 }
1574             }
1575         }
1576 
computeWeight(double start, double end, boolean isContextSwitch, int pixel)1577         private double computeWeight(double start, double end,
1578                 boolean isContextSwitch, int pixel) {
1579             if (isContextSwitch) {
1580                 return 0;
1581             }
1582             double pixelStartFraction = mScaleInfo.valueToPixelFraction(start);
1583             double pixelEndFraction = mScaleInfo.valueToPixelFraction(end);
1584             double leftEndPoint = Math.max(pixelStartFraction, pixel - 0.5);
1585             double rightEndPoint = Math.min(pixelEndFraction, pixel + 0.5);
1586             double weight = rightEndPoint - leftEndPoint;
1587             return weight;
1588         }
1589 
emitPixelStrip(RowData rd, int y, Pixel pixel)1590         private void emitPixelStrip(RowData rd, int y, Pixel pixel) {
1591             Strip strip;
1592 
1593             if (pixel.mSegment == null)
1594                 return;
1595 
1596             int x = pixel.mStart + LeftMargin;
1597             // Compute the percentage of the row height proportional to
1598             // the weight of this pixel. But don't let the proportion
1599             // exceed 3/4 of the row height so that we can easily see
1600             // if a given time range includes more than one method.
1601             int height = (int) (pixel.mMaxWeight * rowHeight * 0.75);
1602             if (height < mMinStripHeight)
1603                 height = mMinStripHeight;
1604             int remainder = rowHeight - height;
1605             if (remainder > 0) {
1606                 strip = new Strip(x, y, 1, remainder, rd, pixel.mSegment,
1607                         mFadeColors ? mColorGray : mColorBlack);
1608                 mStripList.add(strip);
1609                 // System.out.printf("emitPixel (%d, %d) height %d black\n",
1610                 // x, y, remainder);
1611             }
1612             strip = new Strip(x, y + remainder, 1, height, rd, pixel.mSegment,
1613                     pixel.mColor);
1614             mStripList.add(strip);
1615             // System.out.printf("emitPixel (%d, %d) height %d %s\n",
1616             // x, y + remainder, height, pixel.segment.block.getName());
1617             pixel.mSegment = null;
1618             pixel.mMaxWeight = 0.0;
1619         }
1620 
mouseMove(MouseEvent me)1621         private void mouseMove(MouseEvent me) {
1622             if (false) {
1623                 if (mHighlightMethodData != null) {
1624                     mHighlightMethodData = null;
1625                     // Force a recomputation of the strip colors
1626                     mCachedEndRow = -1;
1627                 }
1628             }
1629             Point dim = mSurface.getSize();
1630             int x = me.x;
1631             if (x < LeftMargin)
1632                 x = LeftMargin;
1633             if (x > dim.x - RightMargin)
1634                 x = dim.x - RightMargin;
1635             mMouse.x = x;
1636             mMouse.y = me.y;
1637             mTimescale.setVbarPosition(x);
1638             if (mGraphicsState == GraphicsState.Marking) {
1639                 mTimescale.setMarkEnd(x);
1640             }
1641 
1642             if (mGraphicsState == GraphicsState.Normal) {
1643                 // Set the cursor to the normal state.
1644                 mSurface.setCursor(mNormalCursor);
1645             } else if (mGraphicsState == GraphicsState.Marking) {
1646                 // Make the cursor point in the direction of the sweep
1647                 if (mMouse.x >= mMouseMarkStartX)
1648                     mSurface.setCursor(mIncreasingCursor);
1649                 else
1650                     mSurface.setCursor(mDecreasingCursor);
1651             }
1652             int rownum = (mMouse.y + mScrollOffsetY) / rowYSpace;
1653             if (me.y < 0 || me.y >= dim.y) {
1654                 rownum = -1;
1655             }
1656             if (mMouseRow != rownum) {
1657                 mMouseRow = rownum;
1658                 mLabels.redraw();
1659             }
1660             redraw();
1661         }
1662 
mouseDown(MouseEvent me)1663         private void mouseDown(MouseEvent me) {
1664             Point dim = mSurface.getSize();
1665             int x = me.x;
1666             if (x < LeftMargin)
1667                 x = LeftMargin;
1668             if (x > dim.x - RightMargin)
1669                 x = dim.x - RightMargin;
1670             mMouseMarkStartX = x;
1671             mGraphicsState = GraphicsState.Marking;
1672             mSurface.setCursor(mIncreasingCursor);
1673             mTimescale.setMarkStart(mMouseMarkStartX);
1674             mTimescale.setMarkEnd(mMouseMarkStartX);
1675             redraw();
1676         }
1677 
mouseUp(MouseEvent me)1678         private void mouseUp(MouseEvent me) {
1679             mSurface.setCursor(mNormalCursor);
1680             if (mGraphicsState != GraphicsState.Marking) {
1681                 mGraphicsState = GraphicsState.Normal;
1682                 return;
1683             }
1684             mGraphicsState = GraphicsState.Animating;
1685             Point dim = mSurface.getSize();
1686 
1687             // If the user released the mouse outside the drawing area then
1688             // cancel the zoom.
1689             if (me.y <= 0 || me.y >= dim.y) {
1690                 mGraphicsState = GraphicsState.Normal;
1691                 redraw();
1692                 return;
1693             }
1694 
1695             int x = me.x;
1696             if (x < LeftMargin)
1697                 x = LeftMargin;
1698             if (x > dim.x - RightMargin)
1699                 x = dim.x - RightMargin;
1700             mMouseMarkEndX = x;
1701 
1702             // If the user clicked and released the mouse at the same point
1703             // (+/- a pixel or two) then cancel the zoom (but select the
1704             // method).
1705             int dist = mMouseMarkEndX - mMouseMarkStartX;
1706             if (dist < 0)
1707                 dist = -dist;
1708             if (dist <= 2) {
1709                 mGraphicsState = GraphicsState.Normal;
1710 
1711                 // Select the method underneath the mouse
1712                 mMouseSelect.x = mMouseMarkStartX;
1713                 mMouseSelect.y = me.y;
1714                 redraw();
1715                 return;
1716             }
1717 
1718             // Make mouseEndX be the higher end point
1719             if (mMouseMarkEndX < mMouseMarkStartX) {
1720                 int temp = mMouseMarkEndX;
1721                 mMouseMarkEndX = mMouseMarkStartX;
1722                 mMouseMarkStartX = temp;
1723             }
1724 
1725             // If the zoom area is the whole window (or nearly the whole
1726             // window) then cancel the zoom.
1727             if (mMouseMarkStartX <= LeftMargin + MinZoomPixelMargin
1728                     && mMouseMarkEndX >= dim.x - RightMargin - MinZoomPixelMargin) {
1729                 mGraphicsState = GraphicsState.Normal;
1730                 redraw();
1731                 return;
1732             }
1733 
1734             // Compute some variables needed for zooming.
1735             // It's probably easiest to explain by an example. There
1736             // are two scales (or dimensions) involved: one for the pixels
1737             // and one for the values (microseconds). To keep the example
1738             // simple, suppose we have pixels in the range [0,16] and
1739             // values in the range [100, 260], and suppose the user
1740             // selects a zoom window from pixel 4 to pixel 8.
1741             //
1742             // usec: 100 140 180 260
1743             // |-------|ZZZZZZZ|---------------|
1744             // pixel: 0 4 8 16
1745             //
1746             // I've drawn the pixels starting at zero for simplicity, but
1747             // in fact the drawable area is offset from the left margin
1748             // by the value of "LeftMargin".
1749             //
1750             // The "pixels-per-range" (ppr) in this case is 0.1 (a tenth of
1751             // a pixel per usec). What we want is to redraw the screen in
1752             // several steps, each time increasing the zoom window until the
1753             // zoom window fills the screen. For simplicity, assume that
1754             // we want to zoom in four equal steps. Then the snapshots
1755             // of the screen at each step would look something like this:
1756             //
1757             // usec: 100 140 180 260
1758             // |-------|ZZZZZZZ|---------------|
1759             // pixel: 0 4 8 16
1760             //
1761             // usec: ? 140 180 ?
1762             // |-----|ZZZZZZZZZZZZZ|-----------|
1763             // pixel: 0 3 10 16
1764             //
1765             // usec: ? 140 180 ?
1766             // |---|ZZZZZZZZZZZZZZZZZZZ|-------|
1767             // pixel: 0 2 12 16
1768             //
1769             // usec: ?140 180 ?
1770             // |-|ZZZZZZZZZZZZZZZZZZZZZZZZZ|---|
1771             // pixel: 0 1 14 16
1772             //
1773             // usec: 140 180
1774             // |ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ|
1775             // pixel: 0 16
1776             //
1777             // The problem is how to compute the endpoints (denoted by ?)
1778             // for each step. This is a little tricky. We first need to
1779             // compute the "fixed point": this is the point in the selection
1780             // that doesn't move left or right. Then we can recompute the
1781             // "ppr" (pixels per range) at each step and then find the
1782             // endpoints. The computation of the end points is done
1783             // in animateZoom(). This method computes the fixed point
1784             // and some other variables needed in animateZoom().
1785 
1786             double minVal = mScaleInfo.getMinVal();
1787             double maxVal = mScaleInfo.getMaxVal();
1788             double ppr = mScaleInfo.getPixelsPerRange();
1789             mZoomMin = minVal + ((mMouseMarkStartX - LeftMargin) / ppr);
1790             mZoomMax = minVal + ((mMouseMarkEndX - LeftMargin) / ppr);
1791 
1792             // Clamp the min and max values to the actual data min and max
1793             if (mZoomMin < mMinDataVal)
1794                 mZoomMin = mMinDataVal;
1795             if (mZoomMax > mMaxDataVal)
1796                 mZoomMax = mMaxDataVal;
1797 
1798             // Snap the min and max points to the grid determined by the
1799             // TickScaler
1800             // before we zoom.
1801             int xdim = dim.x - TotalXMargin;
1802             TickScaler scaler = new TickScaler(mZoomMin, mZoomMax, xdim,
1803                     PixelsPerTick);
1804             scaler.computeTicks(false);
1805             mZoomMin = scaler.getMinVal();
1806             mZoomMax = scaler.getMaxVal();
1807 
1808             // Also snap the mouse points (in pixel space) to be consistent with
1809             // zoomMin and zoomMax (in value space).
1810             mMouseMarkStartX = (int) ((mZoomMin - minVal) * ppr + LeftMargin);
1811             mMouseMarkEndX = (int) ((mZoomMax - minVal) * ppr + LeftMargin);
1812             mTimescale.setMarkStart(mMouseMarkStartX);
1813             mTimescale.setMarkEnd(mMouseMarkEndX);
1814 
1815             // Compute the mouse selection end point distances
1816             mMouseEndDistance = dim.x - RightMargin - mMouseMarkEndX;
1817             mMouseStartDistance = mMouseMarkStartX - LeftMargin;
1818             mZoomMouseStart = mMouseMarkStartX;
1819             mZoomMouseEnd = mMouseMarkEndX;
1820             mZoomStep = 0;
1821 
1822             // Compute the fixed point in both value space and pixel space.
1823             mMin2ZoomMin = mZoomMin - minVal;
1824             mZoomMax2Max = maxVal - mZoomMax;
1825             mZoomFixed = mZoomMin + (mZoomMax - mZoomMin) * mMin2ZoomMin
1826                     / (mMin2ZoomMin + mZoomMax2Max);
1827             mZoomFixedPixel = (mZoomFixed - minVal) * ppr + LeftMargin;
1828             mFixedPixelStartDistance = mZoomFixedPixel - LeftMargin;
1829             mFixedPixelEndDistance = dim.x - RightMargin - mZoomFixedPixel;
1830 
1831             mZoomMin2Fixed = mZoomFixed - mZoomMin;
1832             mFixed2ZoomMax = mZoomMax - mZoomFixed;
1833 
1834             getDisplay().timerExec(ZOOM_TIMER_INTERVAL, mZoomAnimator);
1835             redraw();
1836             update();
1837         }
1838 
mouseScrolled(MouseEvent me)1839         private void mouseScrolled(MouseEvent me) {
1840             mGraphicsState = GraphicsState.Scrolling;
1841             double tMin = mScaleInfo.getMinVal();
1842             double tMax = mScaleInfo.getMaxVal();
1843             double zoomFactor = 2;
1844             double tMinRef = mLimitMinVal;
1845             double tMaxRef = mLimitMaxVal;
1846             double t; // the fixed point
1847             double tMinNew;
1848             double tMaxNew;
1849             if (me.count > 0) {
1850                 // we zoom in
1851                 Point dim = mSurface.getSize();
1852                 int x = me.x;
1853                 if (x < LeftMargin)
1854                     x = LeftMargin;
1855                 if (x > dim.x - RightMargin)
1856                     x = dim.x - RightMargin;
1857                 double ppr = mScaleInfo.getPixelsPerRange();
1858                 t = tMin + ((x - LeftMargin) / ppr);
1859                 tMinNew = Math.max(tMinRef, t - (t - tMin) / zoomFactor);
1860                 tMaxNew = Math.min(tMaxRef, t + (tMax - t) / zoomFactor);
1861             } else {
1862                 // we zoom out
1863                 double factor = (tMax - tMin) / (tMaxRef - tMinRef);
1864                 if (factor < 1) {
1865                     t = (factor * tMinRef - tMin) / (factor - 1);
1866                     tMinNew = Math.max(tMinRef, t - zoomFactor * (t - tMin));
1867                     tMaxNew = Math.min(tMaxRef, t + zoomFactor * (tMax - t));
1868                 } else {
1869                     return;
1870                 }
1871             }
1872             mScaleInfo.setMinVal(tMinNew);
1873             mScaleInfo.setMaxVal(tMaxNew);
1874             mSurface.redraw();
1875         }
1876 
1877         // No defined behavior yet for double-click.
mouseDoubleClick(MouseEvent me)1878         private void mouseDoubleClick(MouseEvent me) {
1879         }
1880 
startScaling(int mouseX)1881         public void startScaling(int mouseX) {
1882             Point dim = mSurface.getSize();
1883             int x = mouseX;
1884             if (x < LeftMargin)
1885                 x = LeftMargin;
1886             if (x > dim.x - RightMargin)
1887                 x = dim.x - RightMargin;
1888             mMouseMarkStartX = x;
1889             mGraphicsState = GraphicsState.Scaling;
1890             mScalePixelsPerRange = mScaleInfo.getPixelsPerRange();
1891             mScaleMinVal = mScaleInfo.getMinVal();
1892             mScaleMaxVal = mScaleInfo.getMaxVal();
1893         }
1894 
stopScaling(int mouseX)1895         public void stopScaling(int mouseX) {
1896             mGraphicsState = GraphicsState.Normal;
1897         }
1898 
animateHighlight()1899         private void animateHighlight() {
1900             mHighlightStep += 1;
1901             if (mHighlightStep >= HIGHLIGHT_STEPS) {
1902                 mFadeColors = false;
1903                 mHighlightStep = 0;
1904                 // Force a recomputation of the strip colors
1905                 mCachedEndRow = -1;
1906             } else {
1907                 mFadeColors = true;
1908                 mShowHighlightName = true;
1909                 mHighlightHeight = highlightHeights[mHighlightStep];
1910                 getDisplay().timerExec(HIGHLIGHT_TIMER_INTERVAL, mHighlightAnimator);
1911             }
1912             redraw();
1913         }
1914 
clearHighlights()1915         private void clearHighlights() {
1916             // System.out.printf("clearHighlights()\n");
1917             mShowHighlightName = false;
1918             mHighlightHeight = 0;
1919             mHighlightMethodData = null;
1920             mHighlightCall = null;
1921             mFadeColors = false;
1922             mHighlightStep = 0;
1923             // Force a recomputation of the strip colors
1924             mCachedEndRow = -1;
1925             redraw();
1926         }
1927 
animateZoom()1928         private void animateZoom() {
1929             mZoomStep += 1;
1930             if (mZoomStep > ZOOM_STEPS) {
1931                 mGraphicsState = GraphicsState.Normal;
1932                 // Force a normal recomputation
1933                 mCachedMinVal = mScaleInfo.getMinVal() + 1;
1934             } else if (mZoomStep == ZOOM_STEPS) {
1935                 mScaleInfo.setMinVal(mZoomMin);
1936                 mScaleInfo.setMaxVal(mZoomMax);
1937                 mMouseMarkStartX = LeftMargin;
1938                 Point dim = getSize();
1939                 mMouseMarkEndX = dim.x - RightMargin;
1940                 mTimescale.setMarkStart(mMouseMarkStartX);
1941                 mTimescale.setMarkEnd(mMouseMarkEndX);
1942                 getDisplay().timerExec(ZOOM_TIMER_INTERVAL, mZoomAnimator);
1943             } else {
1944                 // Zoom in slowly at first, then speed up, then slow down.
1945                 // The zoom fractions are precomputed to save time.
1946                 double fraction = mZoomFractions[mZoomStep];
1947                 mMouseMarkStartX = (int) (mZoomMouseStart - fraction * mMouseStartDistance);
1948                 mMouseMarkEndX = (int) (mZoomMouseEnd + fraction * mMouseEndDistance);
1949                 mTimescale.setMarkStart(mMouseMarkStartX);
1950                 mTimescale.setMarkEnd(mMouseMarkEndX);
1951 
1952                 // Compute the new pixels-per-range. Avoid division by zero.
1953                 double ppr;
1954                 if (mZoomMin2Fixed >= mFixed2ZoomMax)
1955                     ppr = (mZoomFixedPixel - mMouseMarkStartX) / mZoomMin2Fixed;
1956                 else
1957                     ppr = (mMouseMarkEndX - mZoomFixedPixel) / mFixed2ZoomMax;
1958                 double newMin = mZoomFixed - mFixedPixelStartDistance / ppr;
1959                 double newMax = mZoomFixed + mFixedPixelEndDistance / ppr;
1960                 mScaleInfo.setMinVal(newMin);
1961                 mScaleInfo.setMaxVal(newMax);
1962 
1963                 getDisplay().timerExec(ZOOM_TIMER_INTERVAL, mZoomAnimator);
1964             }
1965             redraw();
1966         }
1967 
1968         private static final int TotalXMargin = LeftMargin + RightMargin;
1969         private static final int yMargin = 1; // blank space on top
1970         // The minimum margin on each side of the zoom window, in pixels.
1971         private static final int MinZoomPixelMargin = 10;
1972         private GraphicsState mGraphicsState = GraphicsState.Normal;
1973         private Point mMouse = new Point(LeftMargin, 0);
1974         private int mMouseMarkStartX;
1975         private int mMouseMarkEndX;
1976         private boolean mDebug = false;
1977         private ArrayList<Strip> mStripList = new ArrayList<Strip>();
1978         private ArrayList<Range> mHighlightExclusive = new ArrayList<Range>();
1979         private ArrayList<Range> mHighlightInclusive = new ArrayList<Range>();
1980         private int mMinStripHeight = 2;
1981         private double mCachedMinVal;
1982         private double mCachedMaxVal;
1983         private int mCachedStartRow;
1984         private int mCachedEndRow;
1985         private double mScalePixelsPerRange;
1986         private double mScaleMinVal;
1987         private double mScaleMaxVal;
1988         private double mLimitMinVal;
1989         private double mLimitMaxVal;
1990         private double mMinDataVal;
1991         private double mMaxDataVal;
1992         private Cursor mNormalCursor;
1993         private Cursor mIncreasingCursor;
1994         private Cursor mDecreasingCursor;
1995         private static final int ZOOM_TIMER_INTERVAL = 10;
1996         private static final int HIGHLIGHT_TIMER_INTERVAL = 50;
1997         private static final int ZOOM_STEPS = 8; // must be even
1998         private int mHighlightHeight = 4;
1999         private final int[] highlightHeights = { 0, 2, 4, 5, 6, 5, 4, 2, 4, 5,
2000                 6 };
2001         private final int HIGHLIGHT_STEPS = highlightHeights.length;
2002         private boolean mFadeColors;
2003         private boolean mShowHighlightName;
2004         private double[] mZoomFractions;
2005         private int mZoomStep;
2006         private int mZoomMouseStart;
2007         private int mZoomMouseEnd;
2008         private int mMouseStartDistance;
2009         private int mMouseEndDistance;
2010         private Point mMouseSelect = new Point(0, 0);
2011         private double mZoomFixed;
2012         private double mZoomFixedPixel;
2013         private double mFixedPixelStartDistance;
2014         private double mFixedPixelEndDistance;
2015         private double mZoomMin2Fixed;
2016         private double mMin2ZoomMin;
2017         private double mFixed2ZoomMax;
2018         private double mZoomMax2Max;
2019         private double mZoomMin;
2020         private double mZoomMax;
2021         private Runnable mZoomAnimator;
2022         private Runnable mHighlightAnimator;
2023         private int mHighlightStep;
2024     }
2025 
computeVisibleRows(int ydim)2026     private int computeVisibleRows(int ydim) {
2027         // If we resize, then move the bottom row down.  Don't allow the scroll
2028         // to waste space at the bottom.
2029         int offsetY = mScrollOffsetY;
2030         int spaceNeeded = mNumRows * rowYSpace;
2031         if (offsetY + ydim > spaceNeeded) {
2032             offsetY = spaceNeeded - ydim;
2033             if (offsetY < 0) {
2034                 offsetY = 0;
2035             }
2036         }
2037         mStartRow = offsetY / rowYSpace;
2038         mEndRow = (offsetY + ydim) / rowYSpace;
2039         if (mEndRow >= mNumRows) {
2040             mEndRow = mNumRows - 1;
2041         }
2042 
2043         return offsetY;
2044     }
2045 
startHighlighting()2046     private void startHighlighting() {
2047         // System.out.printf("startHighlighting()\n");
2048         mSurface.mHighlightStep = 0;
2049         mSurface.mFadeColors = true;
2050         // Force a recomputation of the color strips
2051         mSurface.mCachedEndRow = -1;
2052         getDisplay().timerExec(0, mSurface.mHighlightAnimator);
2053     }
2054 
2055     private static class RowData {
RowData(Row row)2056         RowData(Row row) {
2057             mName = row.getName();
2058             mStack = new ArrayList<Block>();
2059         }
2060 
push(Block block)2061         public void push(Block block) {
2062             mStack.add(block);
2063         }
2064 
top()2065         public Block top() {
2066             if (mStack.size() == 0)
2067                 return null;
2068             return mStack.get(mStack.size() - 1);
2069         }
2070 
pop()2071         public void pop() {
2072             if (mStack.size() == 0)
2073                 return;
2074             mStack.remove(mStack.size() - 1);
2075         }
2076 
2077         private String mName;
2078         private int mRank;
2079         private long mElapsed;
2080         private long mEndTime;
2081         private ArrayList<Block> mStack;
2082     }
2083 
2084     private static class Segment {
Segment(RowData rowData, Block block, long startTime, long endTime)2085         Segment(RowData rowData, Block block, long startTime, long endTime) {
2086             mRowData = rowData;
2087             if (block.isContextSwitch()) {
2088                 mBlock = block.getParentBlock();
2089                 mIsContextSwitch = true;
2090             } else {
2091                 mBlock = block;
2092             }
2093             mStartTime = startTime;
2094             mEndTime = endTime;
2095         }
2096 
2097         private RowData mRowData;
2098         private Block mBlock;
2099         private long mStartTime;
2100         private long mEndTime;
2101         private boolean mIsContextSwitch;
2102     }
2103 
2104     private static class Strip {
Strip(int x, int y, int width, int height, RowData rowData, Segment segment, Color color)2105         Strip(int x, int y, int width, int height, RowData rowData,
2106                 Segment segment, Color color) {
2107             mX = x;
2108             mY = y;
2109             mWidth = width;
2110             mHeight = height;
2111             mRowData = rowData;
2112             mSegment = segment;
2113             mColor = color;
2114         }
2115 
2116         int mX;
2117         int mY;
2118         int mWidth;
2119         int mHeight;
2120         RowData mRowData;
2121         Segment mSegment;
2122         Color mColor;
2123     }
2124 
2125     private static class Pixel {
setFields(int start, double weight, Segment segment, Color color, RowData rowData)2126         public void setFields(int start, double weight, Segment segment,
2127                 Color color, RowData rowData) {
2128             mStart = start;
2129             mMaxWeight = weight;
2130             mSegment = segment;
2131             mColor = color;
2132             mRowData = rowData;
2133         }
2134 
2135         int mStart = -2; // some value that won't match another pixel
2136         double mMaxWeight;
2137         Segment mSegment;
2138         Color mColor; // we need the color here because it may be faded
2139         RowData mRowData;
2140     }
2141 
2142     private static class Range {
Range(int xStart, int width, int y, Color color)2143         Range(int xStart, int width, int y, Color color) {
2144             mXdim.x = xStart;
2145             mXdim.y = width;
2146             mY = y;
2147             mColor = color;
2148         }
2149 
2150         Point mXdim = new Point(0, 0);
2151         int mY;
2152         Color mColor;
2153     }
2154 }
2155