VisualVM
  1. VisualVM
  2. VISUALVM-478

spikey and sometimes scattered graph

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.3.2, 1.3.3
    • Fix Version/s: 1.3.8
    • Component/s: code
    • Labels:
      None
    • Environment:

      any

      Description

      Once there are more data values than pixels the graphs are very spikey.
      Also sometimes the graph is completely broken.

      Find attached some code changes which provide a way steadier graph.

      The broken graph results from bogus handling of the clipping rect. That bug is inside the Netbeans code.

      1. XYPainter.java
        15 kB
        bebbo
      1. graph.gif
        24 kB
      2. graph1.gif
        24 kB
      3. graph2.gif
        21 kB

        Activity

        Hide
        bebbo added a comment -

        More improved version - max details and stable graph

        Show
        bebbo added a comment - More improved version - max details and stable graph
        Hide
        jsedlacek added a comment -

        Thanks for your fix! Actually the current code works as designed, it's a low-quality painter with extremely low overhead which paints in almost constant time no matter how many items are to be displayed.

        Your change implements a better-quality painter which is more sensitive to items count in terms of performance. We're thinking of providing more painters to give the user a choice between performance and display quality, you code might be a basis for the other painters.

        BTW your changes are hard to read, there's a lot of whitespace and formatting changes in the file which are unrelated to the algorithm modifications. A simple diff without reformatting would be easier to understand.

        Show
        jsedlacek added a comment - Thanks for your fix! Actually the current code works as designed, it's a low-quality painter with extremely low overhead which paints in almost constant time no matter how many items are to be displayed. Your change implements a better-quality painter which is more sensitive to items count in terms of performance. We're thinking of providing more painters to give the user a choice between performance and display quality, you code might be a basis for the other painters. BTW your changes are hard to read, there's a lot of whitespace and formatting changes in the file which are unrelated to the algorithm modifications. A simple diff without reformatting would be easier to understand.
        Hide
        bebbo added a comment - - edited

        I am not sure if it works as intented.

        Have a look at the screenshots taken in intervals of few seconds.

        1. graph vs graph1: The shape shifts with each tick. Graphs appear or vanish.
        2. graph vs graph2: Sometimes the display is screwed up - paint occurs with bogus offset.

        Show
        bebbo added a comment - - edited I am not sure if it works as intented. Have a look at the screenshots taken in intervals of few seconds. 1. graph vs graph1: The shape shifts with each tick. Graphs appear or vanish. 2. graph vs graph2: Sometimes the display is screwed up - paint occurs with bogus offset.
        Hide
        bebbo added a comment -

        The cause for broken graphs

        The broken graphs results from wrong calculation of deltas in TransformableCanvasComponent.setDataBounds(...) which is invoked when the data starts shifting.

        Since the values for dx (and dy?) are totaly bogus, I provide a patch which omitts the shifting since dx and dy are set to 0.

        diff -r d7d9af603a28 lib.profiler.charts/src/org/netbeans/lib/profiler/charts/canvas/TransformableCanvasComponent.java
        --- a/lib.profiler.charts/src/org/netbeans/lib/profiler/charts/canvas/TransformableCanvasComponent.java	Fri Nov 18 04:06:47 2011 +0100
        +++ b/lib.profiler.charts/src/org/netbeans/lib/profiler/charts/canvas/TransformableCanvasComponent.java	Tue Dec 06 20:18:44 2011 +0100
        @@ -430,9 +430,12 @@
                     dataBoundsChanged(dataOffsetX, dataOffsetY, dataWidth, dataHeight,
                                       oldDataOffsetX, oldDataOffsetY, oldDataWidth, oldDataHeight);
         
        -            dx = dxx + (oldContentsOffsetX - contentsOffsetX) - (offsetX - oldOffsetX);
        -            dy = dyy + (oldContentsOffsetY - contentsOffsetY) - (offsetY - oldOffsetY);
        +//            dx = dxx + (oldContentsOffsetX - contentsOffsetX) - (offsetX - oldOffsetX);
        +//            dy = dyy + (oldContentsOffsetY - contentsOffsetY) - (offsetY - oldOffsetY);
         
        +            // shifting does not work correctly - set it to zero
        +            dx = dy = 0;
        +            
                     oldScaleX = scaleX;
                     oldScaleY = scaleY;
        

        XYPainter.java

        There is only a rewrite of one function. Here is again the code for that rewritten function. (Using a diff-ignore-white-space or reformat with your own formatter would show it too).

            private static int[][] createPoints(XYItem item, Rectangle dirtyArea, SynchronousXYChartContext context, int type,
                    int maxValueOffset) {
        
                // even if this looks like a getter - it updates the context and is needed for mouse over info!
                context.getVisibleBounds(dirtyArea);
                
                int valuesCount = item.getValuesCount();
                double itemValueFactor = type == TYPE_RELATIVE ? getItemValueFactor(context, maxValueOffset,
                        item.getBounds().height) : 0;
                int maxValueIndex = 0;
                int extrema = Integer.MIN_VALUE;
                for (int x = 0; x < valuesCount; ++x) {
                    int v = Utils.checkedInt(Math.ceil(getYValue(item, x, type, context, itemValueFactor)));
                    if (v > extrema) {
                        extrema = v;
                        maxValueIndex = x;
                    }
                }
        
                // more values than visible? use the visible width
                int visibleCount = valuesCount;
                if (visibleCount > context.getViewWidth())
                    visibleCount = (int) context.getViewWidth();
                // use only integers to scale the diagram
                int valsPerPixel = valuesCount / visibleCount;
                if (valuesCount > visibleCount * valsPerPixel) {
                    ++valsPerPixel;
                    visibleCount = valuesCount / valsPerPixel;
                }
        
                // + 2 for the polygon - copy this later to match the clip
                int[] xPoints = new int[visibleCount + 2];
                int[] yPoints = new int[visibleCount + 2];
        
                // calculate an offset to get a stable graph.
                int offset = 0;
                if (valsPerPixel > 1) {
                    offset = (-maxValueIndex % valsPerPixel);
                }
        
                // start from max at median to the right and in 2nd pass to the left
                int median = (maxValueIndex + offset) / valsPerPixel;
                for (int pass = 0, add = 1; pass < 2; ++pass) {
                    int lastExt = Integer.MAX_VALUE;
                    boolean isMax = true;
                    int lastMean = extrema;
                    for (int index = median; index >= 0 && index < visibleCount; index += add) {
                        int dataIndex = index * valsPerPixel;
                        xPoints[index] = Utils.checkedInt(Math.ceil(context.getViewX(item.getXValue(dataIndex))));
                        dataIndex -= offset;
                        // get the min, max and sum for the values per pixel.
                        int min = Integer.MAX_VALUE;
                        int max = Integer.MIN_VALUE;
                        long sum = 0;
                        for (int delta = 0; delta < valsPerPixel; ++delta) {
                            int effectiveIndex = dataIndex + delta;
                            // skip index out of bounds
                            if (effectiveIndex < 0 || effectiveIndex >= valuesCount)
                                continue;
                            int v = Utils
                                    .checkedInt(Math.ceil(getYValue(item, effectiveIndex, type, context, itemValueFactor)));
                            if (v < min)
                                min = v;
                            if (v > max)
                                max = v;
                            sum += v;
                        }
                        if (sum == 0)
                            min = max = lastMean + 0;
        
                        if (isMax) {
                            // the last was a maximum
                            if (max - lastExt > lastExt - min) {
                                // maximum difference wins - patch previous pixel with mean value 
                                yPoints[index - add] = lastMean;
                                yPoints[index] = max;
                                lastExt = max;
                            } else {
                                // first maximum - use it.
                                yPoints[index] = min;
                                lastExt = min;
                                isMax = false;
                            }
                        } else {
                            // last was a minimum
                            if (max - lastExt < lastExt - min) {
                                // minimum difference wins - patch the previous pixel with mean value
                                yPoints[index - add] = lastMean;
                                yPoints[index] = min;
                                lastExt = min;
                            } else {
                                // first minimum - use it
                                yPoints[index] = max;
                                lastExt = max;
                                isMax = true;
                            }
                        }
                        // update the lastMean
                        lastMean = (int) (sum / valsPerPixel);
                    }
                    // change direction
                    add = -add;
                }
        
                return new int[][] { xPoints, yPoints };
            }
        

        The code style differs from your code since I added comments and applied my formatting.

        You can also download a patched visualvm at http://franke.ms/download/visualvm_133b1.zip

        Show
        bebbo added a comment - The cause for broken graphs The broken graphs results from wrong calculation of deltas in TransformableCanvasComponent.setDataBounds(...) which is invoked when the data starts shifting. Since the values for dx (and dy?) are totaly bogus, I provide a patch which omitts the shifting since dx and dy are set to 0. diff -r d7d9af603a28 lib.profiler.charts/src/org/netbeans/lib/profiler/charts/canvas/TransformableCanvasComponent.java --- a/lib.profiler.charts/src/org/netbeans/lib/profiler/charts/canvas/TransformableCanvasComponent.java Fri Nov 18 04:06:47 2011 +0100 +++ b/lib.profiler.charts/src/org/netbeans/lib/profiler/charts/canvas/TransformableCanvasComponent.java Tue Dec 06 20:18:44 2011 +0100 @@ -430,9 +430,12 @@ dataBoundsChanged(dataOffsetX, dataOffsetY, dataWidth, dataHeight, oldDataOffsetX, oldDataOffsetY, oldDataWidth, oldDataHeight); - dx = dxx + (oldContentsOffsetX - contentsOffsetX) - (offsetX - oldOffsetX); - dy = dyy + (oldContentsOffsetY - contentsOffsetY) - (offsetY - oldOffsetY); + // dx = dxx + (oldContentsOffsetX - contentsOffsetX) - (offsetX - oldOffsetX); + // dy = dyy + (oldContentsOffsetY - contentsOffsetY) - (offsetY - oldOffsetY); + // shifting does not work correctly - set it to zero + dx = dy = 0; + oldScaleX = scaleX; oldScaleY = scaleY; XYPainter.java There is only a rewrite of one function. Here is again the code for that rewritten function. (Using a diff-ignore-white-space or reformat with your own formatter would show it too). private static int [][] createPoints(XYItem item, Rectangle dirtyArea, SynchronousXYChartContext context, int type, int maxValueOffset) { // even if this looks like a getter - it updates the context and is needed for mouse over info! context.getVisibleBounds(dirtyArea); int valuesCount = item.getValuesCount(); double itemValueFactor = type == TYPE_RELATIVE ? getItemValueFactor(context, maxValueOffset, item.getBounds().height) : 0; int maxValueIndex = 0; int extrema = Integer .MIN_VALUE; for ( int x = 0; x < valuesCount; ++x) { int v = Utils.checkedInt( Math .ceil(getYValue(item, x, type, context, itemValueFactor))); if (v > extrema) { extrema = v; maxValueIndex = x; } } // more values than visible? use the visible width int visibleCount = valuesCount; if (visibleCount > context.getViewWidth()) visibleCount = ( int ) context.getViewWidth(); // use only integers to scale the diagram int valsPerPixel = valuesCount / visibleCount; if (valuesCount > visibleCount * valsPerPixel) { ++valsPerPixel; visibleCount = valuesCount / valsPerPixel; } // + 2 for the polygon - copy this later to match the clip int [] xPoints = new int [visibleCount + 2]; int [] yPoints = new int [visibleCount + 2]; // calculate an offset to get a stable graph. int offset = 0; if (valsPerPixel > 1) { offset = (-maxValueIndex % valsPerPixel); } // start from max at median to the right and in 2nd pass to the left int median = (maxValueIndex + offset) / valsPerPixel; for ( int pass = 0, add = 1; pass < 2; ++pass) { int lastExt = Integer .MAX_VALUE; boolean isMax = true ; int lastMean = extrema; for ( int index = median; index >= 0 && index < visibleCount; index += add) { int dataIndex = index * valsPerPixel; xPoints[index] = Utils.checkedInt( Math .ceil(context.getViewX(item.getXValue(dataIndex)))); dataIndex -= offset; // get the min, max and sum for the values per pixel. int min = Integer .MAX_VALUE; int max = Integer .MIN_VALUE; long sum = 0; for ( int delta = 0; delta < valsPerPixel; ++delta) { int effectiveIndex = dataIndex + delta; // skip index out of bounds if (effectiveIndex < 0 || effectiveIndex >= valuesCount) continue ; int v = Utils .checkedInt( Math .ceil(getYValue(item, effectiveIndex, type, context, itemValueFactor))); if (v < min) min = v; if (v > max) max = v; sum += v; } if (sum == 0) min = max = lastMean + 0; if (isMax) { // the last was a maximum if (max - lastExt > lastExt - min) { // maximum difference wins - patch previous pixel with mean value yPoints[index - add] = lastMean; yPoints[index] = max; lastExt = max; } else { // first maximum - use it. yPoints[index] = min; lastExt = min; isMax = false ; } } else { // last was a minimum if (max - lastExt < lastExt - min) { // minimum difference wins - patch the previous pixel with mean value yPoints[index - add] = lastMean; yPoints[index] = min; lastExt = min; } else { // first minimum - use it yPoints[index] = max; lastExt = max; isMax = true ; } } // update the lastMean lastMean = ( int ) (sum / valsPerPixel); } // change direction add = -add; } return new int [][] { xPoints, yPoints }; } The code style differs from your code since I added comments and applied my formatting. You can also download a patched visualvm at http://franke.ms/download/visualvm_133b1.zip
        Hide
        bebbo added a comment -
        Thanks for your fix! Actually the current code works as designed, 

        Find my most recent fix here: http://franke.ms/download/visualvm_133b2.zip

        Show
        bebbo added a comment - Thanks for your fix! Actually the current code works as designed, Find my most recent fix here: http://franke.ms/download/visualvm_133b2.zip
        Hide
        bebbo added a comment -

        The bug still exists in 1.3.4.

        To see the bug in action watch this: http://franke.ms/download/20120410_1505_48.avi

        You can download a patched version at http://franke.ms/download/visualvm_134b1.zip

        Show
        bebbo added a comment - The bug still exists in 1.3.4. To see the bug in action watch this: http://franke.ms/download/20120410_1505_48.avi You can download a patched version at http://franke.ms/download/visualvm_134b1.zip
        Hide
        jsedlacek added a comment -

        VisualVM-side fix available in revision 3366, NetBeans-side fix commited in NB repository.

        Show
        jsedlacek added a comment - VisualVM-side fix available in revision 3366, NetBeans-side fix commited in NB repository.

          People

          • Assignee:
            jsedlacek
            Reporter:
            bebbo
          • Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: