Permalink
Browse files

Fixed ProfilingGraph calculation integer overflows and added test class.

Complementary to fix proposed in PR #128 by @otteresk.
  • Loading branch information...
luccioman committed Oct 16, 2017
1 parent 5d1ef8f commit fe75f326d8db7db083313a560a7464b8016249d7
@@ -24,6 +24,8 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import java.util.concurrent.TimeUnit;
import net.yacy.cora.protocol.RequestHeader;
import net.yacy.peers.graphics.ProfilingGraph;
import net.yacy.search.Switchboard;
@@ -54,6 +56,7 @@ public static RasterPlotter respond(@SuppressWarnings("unused") final RequestHea
RasterPlotter graph = ProfilingGraph.performanceGraph(
width, height,
indeSizeCache + " URLS / " + sb.index.RWICount() + " WORDS IN INDEX / " + sb.index.RWIBufferCount() + " WORDS IN CACHE",
600, TimeUnit.SECONDS,
showMemory, showPeers);
return graph;
}
@@ -1,4 +1,4 @@
// plasmaProfiling.java
// ProfilingGraph.java
// (C) 2007 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany
// first published 04.12.2007 on http://yacy.net
//
@@ -28,6 +28,7 @@
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import net.yacy.search.EventTracker;
import net.yacy.search.EventTracker.Event;
@@ -55,7 +56,18 @@ public static long maxPayload(final EventTracker.EClass eventname, final long mi
return max;
}
public static RasterPlotter performanceGraph(final int width, final int height, final String subline, final boolean showMemory, final boolean showPeers) {
/**
*
* @param width width of the graph in pixels
* @param height height of the graph in pixels
* @param subline the eventual graph subtitle. May be null.
* @param maxTime maximum time range
* @param timeUnit the time unit for the time range. When null, default to seconds.
* @param showMemory draw memory usage when true
* @param showPeers draw peer ping when true
* @return a RasterPlotter instance drawn from the event tracker
*/
public static RasterPlotter performanceGraph(final int width, final int height, final String subline, final int maxTime, final TimeUnit timeUnit, final boolean showMemory, final boolean showPeers) {
// find maximum values for automatic graph dimension adoption
final int maxppm = (int) maxPayload(EventTracker.EClass.PPM, 25);
final int maxwords = (int) maxPayload(EventTracker.EClass.WORDCACHE, 12000);
@@ -70,19 +82,19 @@ public static RasterPlotter performanceGraph(final int width, final int height,
final int leftscale = (maxwords > 150000) ? maxwords / 150000 * 20000 : 10000;
final int rightscale = showMemory ? ((maxmbytes > 1500) ? maxmbytes / 1500 * 200 : 100) : Math.max(100, maxppm / 100 * 100);
final int anotscale = 1000;
final int bottomscale = 60;
final int bottomscale = Math.max(1, maxTime / 10);
final int vspace = height - topborder - bottomborder;
final int hspace = width - leftborder - rightborder;
final int maxtime = 600;
ChartPlotter chart = new ChartPlotter(width, height, 0xFFFFFFl, 0x000000l, 0xAAAAAAl, leftborder, rightborder, topborder, bottomborder, "YACY PEER PERFORMANCE: MAIN MEMORY, WORD CACHE AND PAGES/MINUTE (PPM)", subline);
chart.declareDimension(ChartPlotter.DIMENSION_BOTTOM, bottomscale, hspace / (maxtime / bottomscale), -maxtime, 0x000000l, 0xCCCCCCl, "TIME/SECONDS");
chart.declareDimension(ChartPlotter.DIMENSION_LEFT, leftscale, vspace * leftscale / maxwords, 0, 0x008800l, null , "WORDS IN INDEXING CACHE");
chart.declareDimension(ChartPlotter.DIMENSION_BOTTOM, bottomscale, hspace / (maxTime / bottomscale), -maxTime,
0x000000l, 0xCCCCCCl, "TIME/" + timeUnit != null ? timeUnit.toString() : TimeUnit.SECONDS.toString());
chart.declareDimension(ChartPlotter.DIMENSION_LEFT, leftscale, (int)((long)vspace * (long)leftscale / maxwords), 0, 0x008800l, null , "WORDS IN INDEXING CACHE");
if (showMemory) {
chart.declareDimension(ChartPlotter.DIMENSION_RIGHT, rightscale, vspace * rightscale / maxmbytes, 0, 0x0000FFl, 0xCCCCCCl, "MEMORY/MEGABYTE");
chart.declareDimension(ChartPlotter.DIMENSION_RIGHT, rightscale, (int)((long)vspace * (long)rightscale / maxmbytes), 0, 0x0000FFl, 0xCCCCCCl, "MEMORY/MEGABYTE");
} else {
chart.declareDimension(ChartPlotter.DIMENSION_RIGHT, rightscale, vspace * rightscale / Math.max(1, maxppm), 0, 0xFF0000l, 0xCCCCCCl, "INDEXING SPEED/PAGES PER MINUTE");
chart.declareDimension(ChartPlotter.DIMENSION_RIGHT, rightscale, (int)((long)vspace * (long)rightscale / Math.max(1, maxppm)), 0, 0xFF0000l, 0xCCCCCCl, "INDEXING SPEED/PAGES PER MINUTE");
}
chart.declareDimension(ChartPlotter.DIMENSION_ANOT0, anotscale, vspace * anotscale / maxppm, 0, 0x008800l, null , "PPM [PAGES/MINUTE]");
chart.declareDimension(ChartPlotter.DIMENSION_ANOT0, anotscale, (int)((long)vspace * (long)anotscale / maxppm), 0, 0x008800l, null , "PPM [PAGES/MINUTE]");
chart.declareDimension(ChartPlotter.DIMENSION_ANOT1, vspace / 6, vspace / 6, 0, 0x888800l, null , "URL");
chart.declareDimension(ChartPlotter.DIMENSION_ANOT2, 1, 1, 0, 0x888800l, null , "PING");
@@ -120,7 +132,7 @@ public static RasterPlotter performanceGraph(final int width, final int height,
event = events.next();
time = event.getTime() - now;
bytes = ((Long) event.payload).longValue();
x1 = time / 1000.0f;
x1 = timeUnit.convert(time, TimeUnit.MILLISECONDS);
y1 = (int) (bytes / 1024 / 1024);
// the dots don't chart.setColor(0xAAAAFF);
// very nice chart.chartDot(ChartPlotter.DIMENSION_BOTTOM, ChartPlotter.DIMENSION_RIGHT, x1, y1, 2, null, 0);
@@ -141,7 +153,7 @@ public static RasterPlotter performanceGraph(final int width, final int height,
event = events.next();
time = event.getTime() - now;
words = (int) ((Long) event.payload).longValue();
x1 = time / 1000.0f;
x1 = timeUnit.convert(time, TimeUnit.MILLISECONDS);
y1 = words;
chart.setColor(0x228822);
chart.chartDot(ChartPlotter.DIMENSION_BOTTOM, ChartPlotter.DIMENSION_LEFT, x1, y1, 2, null, 315);
@@ -161,7 +173,7 @@ public static RasterPlotter performanceGraph(final int width, final int height,
event = events.next();
time = event.getTime() - now;
ppm = (int) ((Long) event.payload).longValue();
x1 = time / 1000.0f;
x1 = timeUnit.convert(time, TimeUnit.MILLISECONDS);
y1 = ppm;
chart.setColor(0xAA8888);
if (x0 < 0) chart.chartLine(ChartPlotter.DIMENSION_BOTTOM, ChartPlotter.DIMENSION_ANOT0, x0, y0, x1, y1);
@@ -183,7 +195,7 @@ public static RasterPlotter performanceGraph(final int width, final int height,
event = events.next();
time = event.getTime() - now;
ping = (EventPing) event.payload;
x1 = time / 1000.0f;
x1 = timeUnit.convert(time, TimeUnit.MILLISECONDS);
y1 = Math.abs((ping.outgoing ? ping.toPeer : ping.fromPeer).hashCode()) % vspace;
pingPeer = ping.outgoing ? "-> " + ping.toPeer.toUpperCase() : "<- " + ping.fromPeer.toUpperCase();
chart.setColor(0x9999AA);
@@ -98,17 +98,17 @@ public void declareDimension(final int dimensionType, final int scale, final int
public void chartDot(final int dimension_x, final int dimension_y, final float coord_x, final int coord_y, final int dotsize, final String anot, final int anotAngle) {
final int x = (int) ((coord_x - this.offsets[dimension_x]) * this.pixels[dimension_x] / this.scales[dimension_x]);
assert this.scales[dimension_y] != 0;
final int y = (int)((long)(coord_y - this.offsets[dimension_y]) * (long)(this.pixels[dimension_y]) / (long)(this.scales[dimension_y]));
final int y = (int)((long)(coord_y - this.offsets[dimension_y]) * (long)(this.pixels[dimension_y]) / (this.scales[dimension_y]));
if (dotsize == 1) plot(this.leftborder + x, this.height - this.bottomborder - y, 100);
else dot(this.leftborder + x, this.height - this.bottomborder - y, dotsize, true, 100);
if (anot != null) PrintTool.print(this, this.leftborder + x + dotsize + 2 + ((anotAngle == 315) ? -9 : 0), this.height - this.bottomborder - y + ((anotAngle == 315) ? -3 : 0), anotAngle, anot, (anotAngle == 0) ? (anot.length() * 6 + x > this.width ? 1 : -1) : ((anotAngle == 315) ? 1 : 0), 100);
}
public void chartLine(final int dimension_x, final int dimension_y, final float coord_x1, final int coord_y1, final float coord_x2, final int coord_y2) {
final int x1 = (int) ((coord_x1 - this.offsets[dimension_x]) * this.pixels[dimension_x] / this.scales[dimension_x]);
final int y1 = (int)((long)(coord_y1 - this.offsets[dimension_y]) * (long)(this.pixels[dimension_y]) / (long)(this.scales[dimension_y]));
final int y1 = (int)((long)(coord_y1 - this.offsets[dimension_y]) * (long)(this.pixels[dimension_y]) / this.scales[dimension_y]);
final int x2 = (int) ((coord_x2 - this.offsets[dimension_x]) * this.pixels[dimension_x] / this.scales[dimension_x]);
final int y2 = (int)((long)(coord_y2 - this.offsets[dimension_y]) * (long)(this.pixels[dimension_y]) / (long)(this.scales[dimension_y]));
final int y2 = (int)((long)(coord_y2 - this.offsets[dimension_y]) * (long)(this.pixels[dimension_y]) / this.scales[dimension_y]);
line(this.leftborder + x1, this.height - this.bottomborder - y1, this.leftborder + x2, this.height - this.bottomborder - y2, 100);
}
@@ -179,36 +179,55 @@ private void drawVerticalScale(final boolean left, final int scale, final int pi
line(x, this.topborder - 4, x, this.height - this.bottomborder + 4, 100);
}
/**
* Write a test chart to a temporary file testimage.png
*/
public static void main(final String[] args) {
System.setProperty("java.awt.headless", "true");
final long bg = 0xFFFFFF;
final long fg = 0x000000;
final long scale = 0xCCCCCC;
final long green = 0x008800;
final long blue = 0x0000FF;
final ChartPlotter ip = new ChartPlotter(660, 240, bg, fg, fg, 30, 30, 20, 20, "PEER PERFORMANCE GRAPH: PAGES/MINUTE and USED MEMORY", "");
ip.declareDimension(DIMENSION_BOTTOM, 60, 60, -600, fg, scale, "TIME/SECONDS");
//ip.declareDimension(DIMENSION_TOP, 10, 40, "000000", null, "count");
ip.declareDimension(DIMENSION_LEFT, 50, 40, 0, green, scale , "PPM [PAGES/MINUTE]");
ip.declareDimension(DIMENSION_RIGHT, 100, 20, 0, blue, scale, "MEMORY/MEGABYTE");
/* Draw an ascending line of 10 plots */
ip.setColor(green);
ip.chartDot(DIMENSION_BOTTOM, DIMENSION_LEFT, -160, 100, 5, null, 0);
ip.chartLine(DIMENSION_BOTTOM, DIMENSION_LEFT, -160, 100, -130, 200);
final int width = 600, maxPPM = 240;
int steps = 10, x = - width;
int ppm = (int)(maxPPM * 0.1);
int ppmStep = (int)((maxPPM * 0.9) / steps);
for(int step = 0; step < steps; step++) {
ip.chartDot(DIMENSION_BOTTOM, DIMENSION_LEFT, x, ppm, 5, null, 0);
ip.chartLine(DIMENSION_BOTTOM, DIMENSION_LEFT, x, ppm, x + (width / steps), ppm + ppmStep);
ppm += ppmStep;
x += (width / steps);
}
/* Draw a descending line of 20 plots */
ip.setColor(blue);
ip.chartDot(DIMENSION_BOTTOM, DIMENSION_RIGHT, -50, 300, 2, null, 0);
ip.chartLine(DIMENSION_BOTTOM, DIMENSION_RIGHT, -80, 100, -50, 300);
//ip.print(100, 100, 0, "TEXT", true);
//ip.print(100, 100, 0, "1234", false);
//ip.print(100, 100, 90, "TEXT", true);
//ip.print(100, 100, 90, "1234", false);
final File file = new File(System.getProperty("java.io.tmpdir") + File.separator + "testimage.png");
steps = 20;
final int maxMBytes = 800;
int mBytes = (int)(maxMBytes * 0.8);
int mBytesStep = (int)((maxMBytes * 0.6) / steps);
x = - width;
for(int step = 0; step < steps; step++) {
ip.chartDot(DIMENSION_BOTTOM, DIMENSION_RIGHT, x, mBytes, 5, null, 0);
ip.chartLine(DIMENSION_BOTTOM, DIMENSION_RIGHT, x, mBytes, x + (width / steps), mBytes - mBytesStep);
mBytes -= mBytesStep;
x += (width / steps);
}
final File file = new File(System.getProperty("java.io.tmpdir"),"testimage.png");
try (
/* Automatically closed by this try-with-resources statement */
final FileOutputStream fos = new FileOutputStream(file);
) {
System.out.println("Writing file " + file);
fos.write(RasterPlotter.exportImage(ip.getImage(), "png").getBytes());
//ImageIO.write(ip.getImage(), "png", fos);
System.out.println("CharPlotter test file written at " + file);
} catch (final IOException e) {
e.printStackTrace();
}
@@ -0,0 +1,108 @@
// ProfilingGraphTest.java
// Copyright 2017 by luccioman; https://github.com/luccioman
//
// This is a part of YaCy, a peer-to-peer based web search engine
//
// LICENSE
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package net.yacy.peers.graphics;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import net.yacy.cora.util.ConcurrentLog;
import net.yacy.search.EventTracker;
import net.yacy.visualization.RasterPlotter;
/**
* Unit tests for the {@link ProfilingGraph} class
*/
public class ProfilingGraphTest {
/**
* Generate a performance graph and write it to a temporary file for manual
* viasual checking.<br/>
* Note : it is not an automated JUnit test function as it is time dependant.
*
* @throws IOException
* when a read/write exception occurred
* @throws InterruptedException
* when interrupted before termination
*/
public static void main(final String args[]) throws IOException, InterruptedException {
long time = System.currentTimeMillis();
final long beginTime = time;
long prevTime = time;
/* Feed the event tracker with test values */
final int steps = 100;
/* Ascending memory usage from 500MB to 16GB */
long bytes = 500L * 1024L * 1024L;
final long bytesStep = (16L * 1204L * 1204L * 1204L) / steps;
/* Descending words, from max integer value to zero.
* (events values are stored as long, but currently the actual maximum possible value ford WORDCACHE is an Integer.MAX_VALUE) */
long words = Integer.MAX_VALUE;
final long wordsStep = words / steps;
for (int step = 0; step < steps; step++) {
if ((step % 30) == 0) {
/* Stable PPRM and peer ping values */
EventTracker.update(EventTracker.EClass.PPM, Long.valueOf(500), false);
EventTracker.update(EventTracker.EClass.PEERPING,
new ProfilingGraph.EventPing("localPeerName", "aaaa", true, 1536), false);
}
EventTracker.update(EventTracker.EClass.WORDCACHE, Long.valueOf(words), false);
EventTracker.update(EventTracker.EClass.MEMORY, Long.valueOf(bytes), false);
time = System.currentTimeMillis();
/* Ensure each test event is separated at least from 1ms */
while (time == prevTime) {
Thread.sleep(1);
time = System.currentTimeMillis();
}
prevTime = time;
bytes += bytesStep;
words -= wordsStep;
}
long timeRange = (time - beginTime) * 2;
/* Parameters likely to be encountered on the PerformanceGraph calling class */
final int indexSizeCache = 865749;
final int rwiCount = 512378;
final int rwiBufferCount = 6754;
final RasterPlotter graph = ProfilingGraph.performanceGraph(660, 240,
indexSizeCache + " URLS / " + rwiCount + " WORDS IN INDEX / " + rwiBufferCount + " WORDS IN CACHE",
(int) timeRange, TimeUnit.MILLISECONDS, true, true);
/* Now write the result to a temporary file for visual checking */
final File outputFile = new File(System.getProperty("java.io.tmpdir"), "testPerformanceGraph.png");
try (
/* Automatically closed by this try-with-resources statement */
final FileOutputStream fos = new FileOutputStream(outputFile);) {
fos.write(RasterPlotter.exportImage(graph.getImage(), "png").getBytes());
System.out.println("Performance graph writtent to file " + outputFile);
} finally {
ConcurrentLog.shutdown();
}
}
}

1 comment on commit fe75f32

@smokingwheels

This comment has been minimized.

smokingwheels commented on fe75f32 Oct 20, 2017

Wow

Please sign in to comment.