/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.utils.threads.logger; import java.io.DataInput; import java.io.DataInputStream; import java.io.EOFException; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; public class ThreadLogVisualizer { // Do not show tasks shorter than 5ms final public static long DISCARD_LIMIT = 2 * 1000 * 1000; // 1s columns final public static long COLUMN_WIDTH = 1000000000; final int onlyOneThread = -1; final String colors[] = { "#53c0a7", "#ca49a1", "#64b74e", "#a159ca", "#b6b345", "#656bc5", "#da943b", "#5e99d3", "#d1592b", "#418a53", "#e16182", "#777d34", "#ca89ca", "#b7754c", "#9e4b6b", "#cb4347" }; class Task implements Comparable { String name; long beginTime; long endTime; long threadId; long combined = 0; public Task(String name, long threadId, long beginTime, long endTime) { if(name.length() > 100) name = name.substring(0, 99); this.name = name; this.threadId = threadId; this.beginTime = beginTime; this.endTime = endTime; } @Override // Differences will not fit into int public int compareTo(Task o) { if(beginTime > o.beginTime) return 1; else if(beginTime == o.beginTime) return -1; else return -1; } } ArrayList tasks = new ArrayList(); private boolean acceptThread(long threadId) { if(onlyOneThread == -1) return true; else return (threadId&15) == onlyOneThread; } private Map compositeTasks = new HashMap<>(); public void read(DataInput input) { try { while(true) { try { String taskName = input.readUTF(); long threadId = input.readLong(); long beginTime = input.readLong(); long endTime = input.readLong(); if(!acceptThread(threadId)) continue; if((endTime-beginTime) > DISCARD_LIMIT) { tasks.add(new Task(taskName, threadId, beginTime, endTime)); Task t = compositeTasks.remove(threadId); if(t != null) { if((t.endTime-t.beginTime) > DISCARD_LIMIT) { tasks.add(new Task(t.combined + " small tasks", t.threadId, t.beginTime, t.endTime)); } } } else { Task t = compositeTasks.get(threadId); if(t == null) { t = new Task("", threadId, beginTime, endTime); compositeTasks.put(threadId, t); } if(beginTime - t.endTime > DISCARD_LIMIT) { tasks.add(new Task(t.combined + " small tasks", t.threadId, t.beginTime, t.endTime)); t = new Task("", threadId, beginTime, endTime); compositeTasks.put(threadId, t); } t.endTime = endTime; t.combined++; if((t.endTime-t.beginTime) > DISCARD_LIMIT) { tasks.add(new Task(t.combined + " small tasks", t.threadId, t.beginTime, t.endTime)); compositeTasks.remove(threadId); } } } catch(EOFException e) { break; } } } catch(IOException e) { } Collections.sort(tasks); } class Lane { ArrayList tasks = new ArrayList(); long nextTime = 0; } public void visualize3(PrintStream s) { long minTime = Long.MAX_VALUE; long maxTime = Long.MIN_VALUE; ArrayList lanes = new ArrayList(); int laneId = 0; for(Task task : tasks) { Lane lane; minTime = Math.min(minTime, task.beginTime); maxTime = Math.max(maxTime, task.endTime); for(int seek = laneId-1; seek >= 0; --seek) { if(lanes.get(seek).nextTime < task.beginTime) { laneId = seek; } else { break; } } if(laneId < lanes.size()) lane = lanes.get(laneId); else { lane = new Lane(); lanes.add(lane); } lane.tasks.add(task); lane.nextTime = Math.max(task.endTime, task.beginTime+COLUMN_WIDTH); System.out.println(task.name + " -> " + laneId + "[" + task.beginTime + "-" + task.endTime + "]"); laneId++; } double timeScale = 1e-6; double rowHeight = 30.0; Locale locale = Locale.US; int row = lanes.size(); s.println(""); s.println(""); for(long time = minTime ; time < maxTime ; time += 1000000000) { s.printf(locale, "\n", (time-minTime)*timeScale, 0.0, (time-minTime)*timeScale, row*rowHeight); } for(int r = 0;r\n", (task.beginTime-minTime)*timeScale, r*rowHeight, (task.beginTime-minTime)*timeScale, (r+1)*rowHeight); s.printf(locale, "\n", (task.beginTime-minTime)*timeScale, r*rowHeight, (task.endTime-task.beginTime)*timeScale, rowHeight); } for(Task task : lane.tasks) { int time = (int)(1e-6 * (task.endTime-task.beginTime)); s.printf(locale, "%s\n", (task.endTime-minTime)*timeScale, (r+0.8)*rowHeight, Integer.toString(time) + "ms: " + task.name); } } s.println(""); } public void visualize2(PrintStream s) { long minTime = Long.MAX_VALUE; long maxTime = Long.MIN_VALUE; ArrayList lanes = new ArrayList(); for(Task task : tasks) { minTime = Math.min(minTime, task.beginTime); maxTime = Math.max(maxTime, task.endTime); int laneId; for(laneId=0;laneId " + laneId + "[" + task.beginTime + "-" + task.endTime + "]"); } double timeScale = 1e-7*5; double rowHeight = 30.0; Locale locale = Locale.US; int row = lanes.size(); s.println(""); s.println(""); for(long time = minTime ; time < maxTime ; time += 1000000000) { s.printf(locale, "\n", (time-minTime)*timeScale, 0.0, (time-minTime)*timeScale, row*rowHeight); } for(int r = 0;r\n", (task.beginTime-minTime)*timeScale, r*rowHeight, (task.beginTime-minTime)*timeScale, (r+1)*rowHeight); s.printf(locale, "\n", (task.beginTime-minTime)*timeScale, r*rowHeight, (task.endTime-task.beginTime)*timeScale, rowHeight); } for(Task task : lane.tasks) { s.printf(locale, "%s\n", (task.endTime-minTime)*timeScale, (r+0.8)*rowHeight, task.name); } } s.println(""); } public void visualize(PrintStream s) { long minTime = Long.MAX_VALUE; long maxTime = Long.MIN_VALUE; Map threads = new HashMap(); int row = 0; for(Task task : tasks) { minTime = Math.min(minTime, task.beginTime); maxTime = Math.max(maxTime, task.endTime); if(!threads.containsKey(task.threadId)) threads.put(task.threadId, row++); } double timeScale = 1e-7*0.8; double rowHeight = 60.0; Locale locale = Locale.US; int textPos[] = new int[row]; s.println(""); s.println(""); for(long time = minTime ; time < maxTime ; time += 1000000000) { s.printf(locale, "\n", (time-minTime)*timeScale, 0.0, (time-minTime)*timeScale, row*rowHeight); } for(Task task : tasks) { int r = threads.get(task.threadId); s.printf(locale, "\n", (task.beginTime-minTime)*timeScale, r*rowHeight, (task.beginTime-minTime)*timeScale, (r+1)*rowHeight); s.printf(locale, "\n", (task.beginTime-minTime)*timeScale, r*rowHeight, (task.endTime-task.beginTime)*timeScale, rowHeight); } for(Task task : tasks) { int r = threads.get(task.threadId); s.printf(locale, "%s\n", (task.endTime-minTime)*timeScale, (r*3+(textPos[r]++)%2+0.5)*rowHeight/3, task.name); } s.println(""); } public static void main(String[] args) { try { ThreadLogVisualizer visualizer = new ThreadLogVisualizer(); visualizer.read(new DataInputStream(new FileInputStream(ThreadLogger.LOG_FILE))); visualizer.visualize3(new PrintStream(ThreadLogger.LOG_FILE + ".svg")); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }