package ibis.ipl.registry.statistics; import ibis.util.ThreadPool; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Formatter; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class Statistics implements Runnable { public static final int VERSION = 1; private static final Logger logger = LoggerFactory .getLogger(Statistics.class); private final long start; private long offset; private final String[] opcodes; private final double[] totalTimes; private final long[] incomingRequestCounter; private final long[] outgoingRequestCounter; private final long[] bytesIn; private final long[] bytesOut; private String id; private String poolName; private long writeInterval; List<DataPoint> poolSizeHistory; int currentPoolSize; List<DataPoint> electionEventHistory; private boolean ended = false; public Statistics(String[] opcodes) { this.opcodes = opcodes; this.id = "unknown"; this.poolName = "unknown"; start = System.currentTimeMillis(); offset = 0; totalTimes = new double[opcodes.length]; incomingRequestCounter = new long[opcodes.length]; outgoingRequestCounter = new long[opcodes.length]; bytesIn = new long[opcodes.length]; bytesOut = new long[opcodes.length]; poolSizeHistory = new LinkedList<DataPoint>(); electionEventHistory = new LinkedList<DataPoint>(); currentPoolSize = 0; newPoolSize(0); logger.debug("created statistics"); } public Statistics(File file) throws IOException { DataInputStream in = new DataInputStream(new FileInputStream(file)); int version = in.readInt(); if (version != VERSION) { throw new IOException("cannot read statistics file version: " + version); } start = in.readLong(); offset = in.readLong(); id = in.readUTF(); int nrOfOpcodes = in.readInt(); if (nrOfOpcodes < 0) { throw new IOException("negative number of opcodes"); } opcodes = new String[nrOfOpcodes]; totalTimes = new double[nrOfOpcodes]; incomingRequestCounter = new long[nrOfOpcodes]; outgoingRequestCounter = new long[nrOfOpcodes]; bytesIn = new long[nrOfOpcodes]; bytesOut = new long[nrOfOpcodes]; for (int i = 0; i < nrOfOpcodes; i++) { opcodes[i] = in.readUTF(); totalTimes[i] = in.readDouble(); incomingRequestCounter[i] = in.readLong(); outgoingRequestCounter[i] = in.readLong(); bytesIn[i] = in.readLong(); bytesOut[i] = in.readLong(); } poolSizeHistory = new LinkedList<DataPoint>(); electionEventHistory = new LinkedList<DataPoint>(); int nrOfSizeDataPoints = in.readInt(); if (nrOfSizeDataPoints < 0) { throw new IOException("negative list size"); } for (int i = 0; i < nrOfSizeDataPoints; i++) { poolSizeHistory.add(new DataPoint(in.readLong(), in.readLong())); } int nrOfElectionDataPoints = in.readInt(); if (nrOfElectionDataPoints < 0) { throw new IOException("negative list size"); } for (int i = 0; i < nrOfElectionDataPoints; i++) { electionEventHistory .add(new DataPoint(in.readLong(), in.readLong())); } currentPoolSize = in.readInt(); in.close(); } public synchronized void end() { ended = true; notifyAll(); } public void write() { File file = null; try { ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(byteOut); // write data to array synchronized (this) { out.writeInt(VERSION); out.writeLong(start); out.writeLong(offset); out.writeUTF(id); out.writeInt(opcodes.length); for (int i = 0; i < opcodes.length; i++) { out.writeUTF(opcodes[i]); out.writeDouble(totalTimes[i]); out.writeLong(incomingRequestCounter[i]); out.writeLong(outgoingRequestCounter[i]); out.writeLong(bytesIn[i]); out.writeLong(bytesOut[i]); } out.writeInt(poolSizeHistory.size()); for (DataPoint point : poolSizeHistory) { out.writeLong(point.getTime()); out.writeLong(point.getValue()); } out.writeInt(electionEventHistory.size()); for (DataPoint point : electionEventHistory) { out.writeLong(point.getTime()); out.writeLong(point.getValue()); } out.writeInt(currentPoolSize); out.flush(); out.close(); } // write data to file file = new File("statistics" + File.separator + poolName + File.separator + id); logger.debug("writing statistics to: " + file); if (file.exists()) { file.renameTo(new File(file.getPath() + ".old")); } file.getParentFile().mkdirs(); FileOutputStream fileOut = new FileOutputStream(file); byteOut.writeTo(fileOut); fileOut.flush(); fileOut.close(); } catch (IOException e) { logger.error("cannot write statistics to " + file, e); } logger.debug("DONE writing statistics for: " + id); } public synchronized void add(byte opcode, long time, long bytesReceived, long bytesSend, boolean incoming) { if (opcode >= opcodes.length) { logger.error("unknown opcode in handling stats: " + opcode); } totalTimes[opcode] = totalTimes[opcode] + time; if (incoming) { incomingRequestCounter[opcode]++; } else { outgoingRequestCounter[opcode]++; } bytesIn[opcode] += bytesReceived; bytesOut[opcode] += bytesSend; } synchronized void clear() { for (int i = 0; i < opcodes.length; i++) { totalTimes[i] = 0; incomingRequestCounter[i] = 0; outgoingRequestCounter[i] = 0; } } public synchronized boolean empty() { for (byte i = 0; i < opcodes.length; i++) { if (totalTimes[i] != 0) { return false; } } if (poolSizeHistory.size() > 0) { return false; } if (electionEventHistory.size() > 0) { return false; } return true; } public synchronized void print(Formatter out) { out.format("#statistics for %s\n", id); printCommStats(out); out.format("#total traffic = %.2f MB\n", totalTraffic()); printPoolSizeHistory(out); } public synchronized void printCommStats(Formatter out) { // long totalTraffic = 0; out.format("#communication statistics\n"); out .format("#TYPE IN_COUNT OUT_COUNT BYTES_IN BYTES_OUT TOTAL_TIME AVG_TIME\n"); out .format("# (sec) (ms)\n"); for (byte i = 0; i < opcodes.length; i++) { // totalTraffic += bytesIn[i] + bytesOut[i]; double average = totalTimes[i] / (incomingRequestCounter[i] + outgoingRequestCounter[i]); if (incomingRequestCounter[i] == 0 && outgoingRequestCounter[i] == 0) { average = 0; } out.format("#%-20s %9d %9d %8d %9d %10.2f %10.2f\n", opcodes[i], incomingRequestCounter[i], outgoingRequestCounter[i], bytesIn[i], bytesOut[i], totalTimes[i] / 1000.0, average); } out.format("#distance from server: %d Ms\n", offset); } public synchronized void printPoolSizeHistory(Formatter out) { out.format("#pool size history\n"); out.format("#TIME POOL_SIZE\n"); for (DataPoint point : poolSizeHistory) { double time = ((double) point.getTime() - start + offset) / 1000.0; out.format("%.2f %d\n", time, point.getValue()); } } public synchronized void newPoolSize(int poolSize) { if (!poolSizeHistory.isEmpty()) { long lastPoolSize = poolSizeHistory.get(poolSizeHistory.size() - 1) .getValue(); if (poolSize == lastPoolSize) { // ignore this update, value equal to last return; } } poolSizeHistory.add(new DataPoint(poolSize)); logger.trace("reported pool size now: " + poolSize); } public synchronized void electionEvent() { electionEventHistory .add(new DataPoint(electionEventHistory.size() + 1)); } public synchronized long getStartTime() { return start; } public synchronized long getEndTime() { long result = start; if (poolSizeHistory.size() > 0) { long time = poolSizeHistory.get(poolSizeHistory.size() - 1) .getTime(); if (time > result) { result = time; } } if (electionEventHistory.size() > 0) { long time = electionEventHistory.get( electionEventHistory.size() - 1).getTime(); if (time > result) { result = time; } } return result; } public synchronized long poolSizeAt(long time) { time += offset; if (poolSizeHistory.size() == 0) { return -1; } long result = -1; for (DataPoint point : poolSizeHistory) { if (point.getTime() > time) { // previous point is result return result; } else { result = point.getValue(); } } // return -1 (we don't know) return -1; } public synchronized DataPoint[] getPoolSizeData() { return poolSizeHistory.toArray(new DataPoint[0]); } /** * Total data send/received by the registry (in Mib) */ public synchronized double totalTraffic() { double totalTraffic = 0; for (byte i = 0; i < opcodes.length; i++) { totalTraffic = totalTraffic + bytesIn[i] + bytesOut[i]; } return totalTraffic / 1024.0 / 1024.0; } public synchronized String getID() { return id; } public synchronized void setID(String id, String poolName) { this.id = id; this.poolName = poolName; } public synchronized long getOffset() { return offset; } public synchronized void setOffset(long offset) { this.offset = offset; } public void startWriting(long writeInterval) { this.writeInterval = writeInterval; ThreadPool.createNew(this, "statistics writer"); } public void run() { while (true) { write(); synchronized (this) { try { wait(writeInterval); } catch (InterruptedException e) { // IGNORE } if (ended) { return; } } } } public String toString() { return id; } public static void main(String[] args) throws IOException { Formatter formatter = new Formatter(System.out); for (int i = 0; i < args.length; i++) { File file = new File(args[i]); Statistics statistics = new Statistics(file); statistics.print(formatter); } formatter.flush(); } public synchronized Map<String, String> getMap() { Map<String, String> result = new HashMap<String, String>(); result.put("ibis.id", id); result.put("pool.name", poolName); result.put("current.pool.size", currentPoolSize + ""); double totalTime = 0; int totalInRequests = 0; int totalOutRequests = 0; long totalBytesIn = 0; long totalBytesOut = 0; for (byte i = 0; i < opcodes.length; i++) { totalTime += totalTimes[i]; totalInRequests += incomingRequestCounter[i]; totalOutRequests += outgoingRequestCounter[i]; totalBytesIn += bytesIn[i]; totalBytesOut += bytesOut[i]; } double averageRequestTime = totalTime / (totalInRequests + totalOutRequests); result.put("average.request.time", averageRequestTime + ""); result.put("incoming.requests", totalInRequests + ""); result.put("outgoing.requests", totalOutRequests + ""); result.put("send.bytes", totalBytesOut + ""); result.put("received.bytes", totalBytesIn + ""); return result; } }