--- /dev/null 2016-10-25 08:46:44.038854975 +0200 +++ new/src/share/classes/sun/evtracing/processing/TraceEventJsonWriter.java 2016-10-25 10:40:45.461801544 +0200 @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2014, 2015, Dynatrace and/or its affiliates. All rights reserved. + * + * This file is part of the Lock Contention Tracing Subsystem for the HotSpot + * Virtual Machine, which is developed at Christian Doppler Laboratory on + * Monitoring and Evolution of Very-Large-Scale Software Systems. Please + * contact us at if you need additional information + * or have any questions. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work. If not, see . + * + */ +package sun.evtracing.processing; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import sun.evtracing.parser.GlobalSequenceOrderedEvent; +import sun.evtracing.parser.GroupEvent; +import sun.evtracing.parser.ThreadExitEvent; +import sun.evtracing.parser.ThreadNameChangeEvent; +import sun.evtracing.parser.ThreadParkBeginEvent; +import sun.evtracing.parser.ThreadParkEndEvent; +import sun.evtracing.parser.ThreadStartEvent; +import sun.evtracing.parser.ThreadState; +import sun.evtracing.parser.ThreadStateChangeEvent; +import sun.evtracing.parser.VMEndEvent; +import sun.evtracing.parser.metadata.JavaStack; + +public class TraceEventJsonWriter extends AbstractTraceEventHandler { + private class ThreadData { + private long stateChangeTime = -1; + private ThreadState state; + private final Deque> nestingStack = new ArrayDeque<>(); + + public long stateChangeTime() { + assert stateChangeTime > 0; + return stateChangeTime; + } + + public ThreadState state() { + return state; + } + + public void setThreadState(long time, ThreadState ts) { + this.stateChangeTime = time; + this.state = ts; + } + + public void resetThreadState() { + setThreadState(-1, null); + } + + public int nestingLevel() { + return nestingStack.size(); + } + + public void addParkBeginEvent(ThreadParkBeginEvent event) { + int currentLevel = nestingStack.size(); + assert event.nestingLevel() >= currentLevel; + if (event.nestingLevel() > currentLevel) { + for (int i = currentLevel; i < event.nestingLevel() - 1; i++) { + nestingStack.push(new ArrayList<>()); + } + List events = new ArrayList<>(); + events.add(event); + nestingStack.push(events); + } else if (event.nestingLevel() == currentLevel) { + addParkEvent(event); + } + } + + public void addParkEndEvent(ThreadParkEndEvent event) { + addParkEvent(event); + } + + public void addParkEvent(GlobalSequenceOrderedEvent event) { + if (nestingLevel() > 0) { + nestingStack.peek().add(event); + } else { + event.accept(parkHandler); + } + } + + public long nestingFirstSeen() { + return nestingStack.peek().get(0).timestamp(); + } + + public void popNestingLevel() { + List events = nestingStack.pop(); + if (nestingLevel() > 0) { + nestingStack.peek().addAll(events); + } else { + events.forEach(e -> e.accept(parkHandler)); + } + } + } + + private PrintStream ps; + private final Map threadData = new HashMap<>(); + private final TraceEventHandler parkHandler = new AbstractTraceEventHandler() { + @Override + public void threadParkBegin(ThreadParkBeginEvent event) { + String name = event.clazz().metadata().prettyName(true); + JavaStack jstack = event.stack().metadata(); + String stack; + if (jstack.isSet()) { + stack = StreamSupport.stream(jstack.spliterator(), false) + .map(frame -> "\"" + (frame.method().isSet() ? frame.method().prettyDescriptor(true) : "(unknown)") + "\"") + .collect(Collectors.joining(",")); + } else { + stack = ""; + } + ps.printf(",\n{\"name\":\"%s\",\"cat\":\"park\",\"ph\":\"B\",\"ts\":%d,\"pid\":1,\"tid\":%d,\"args\":{\"Arg isAbsolute\":%s,\"Arg time\":%d,\"Identity Hash Code\":\"%s\",\"Stack\":[%s],\"Nesting Level\":%d,\"Seq Begin\":%d}}", name, event.timestamp() / 1000, event.thread().identifier(), event.isAbsolute(), event.parkTime(), Integer.toHexString(event.blockerObject()), stack, event.nestingLevel(), event.sequenceNumber()); + } + + @Override + public void threadParkEnd(ThreadParkEndEvent event) { + ps.printf(",\n{\"ph\":\"E\",\"ts\":%d,\"pid\":1,\"tid\":%d,\"args\":{\"Seq Unpark\":%d,\"Seq End\":%d,\"Return Code\":\"%s\"}}", event.timestamp() / 1000, event.thread().identifier(), event.unparkSequenceNumber(), event.sequenceNumber(), event.parkReturnCode().name()); + } + }; + + public TraceEventJsonWriter(File file) throws FileNotFoundException { + ps = new PrintStream(file); + ps.println("["); + ps.print("{\"name\":\"process_name\",\"ph\":\"M\",\"pid\":1,\"args\":{\"name\":\"java\"}}"); + } + + private ThreadData getOrCreateThreadData(long thread) { + return threadData.computeIfAbsent(thread, t -> new ThreadData()); + } + + @Override + public void threadStart(ThreadStartEvent event) { + ps.printf(",\n{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":1,\"tid\":%d,\"args\":{\"name\":\"%s\"}}", event.thread().identifier(), event.name()); + } + + @Override + public void threadNameChange(ThreadNameChangeEvent event) { + ps.printf(",\n{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":1,\"tid\":%d,\"args\":{\"name\":\"%s\"}}", event.affectedThread().identifier(), event.newName()); + } + + @Override + public void threadStateChange(ThreadStateChangeEvent event) { + ThreadData td = getOrCreateThreadData(event.affectedThread().identifier()); + if (td.state() != null) { + printThreadState(event.timestamp(), event.affectedThread().identifier(), td); + } + if (!event.newState().toThreadState().equals(Thread.State.TERMINATED)) { + td.setThreadState(event.timestamp(), event.newState()); + } + } + + private void printThreadState(long time, long thread, ThreadData threadData) { + long startTime = threadData.stateChangeTime() / 1000; + long endTime = time / 1000; + if (endTime - startTime > 0) { + ps.printf(",\n{\"name\":\"%s\",\"cat\":\"state\",\"ph\":\"B\",\"ts\":%d,\"pid\":1,\"tid\":%d}", threadData.state().toString(), startTime, thread); + ps.printf(",\n{\"cat\":\"state\",\"ph\":\"E\",\"ts\":%d,\"pid\":1,\"tid\":%d}", endTime, thread); + } + } + + @Override + public void threadExit(ThreadExitEvent event) { + ThreadData td = getOrCreateThreadData(event.thread().identifier()); + if (td.state() != null) { + printThreadState(event.timestamp(), event.thread().identifier(), td); + td.resetThreadState(); + } + } + + @Override + public void endVM(VMEndEvent event) { + ps.println("\n]"); + ps.close(); + } + + @Override + public void threadParkBegin(ThreadParkBeginEvent event) { + ThreadData td = getOrCreateThreadData(event.thread().identifier()); + td.addParkBeginEvent(event); + } + + @Override + public void threadParkEnd(ThreadParkEndEvent event) { + ThreadData td = getOrCreateThreadData(event.thread().identifier()); + td.addParkEndEvent(event); + } + + @Override + public void group(GroupEvent event) { + ThreadData td = getOrCreateThreadData(event.thread().identifier()); + int nestingLevel = td.nestingLevel(); + if (nestingLevel > 0) { + String name = event.clazz().metadata().prettyName(true); + long startTime = td.nestingFirstSeen(); + ps.printf(",\n{\"name\":\"%s\",\"cat\":\"group\",\"ph\":\"X\",\"ts\":%d,\"dur\":%d,\"pid\":1,\"tid\":%d,\"args\":{\"Identity Hash Code\":\"%s\",\"Nesting Level\":%d,\"Seq Begin Reference\":%d,\"Seq End\":%d}}", name, startTime / 1000, (event.timestamp() - startTime) / 1000, event.thread().identifier(), Integer.toHexString(event.obj()), nestingLevel, event.sequenceBeginReference(), event.sequenceNumber()); + td.popNestingLevel(); + } + } + +}