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