--- /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();
+ }
+ }
+
+}