changeset 22905:463553e69619

Add basic functionality for debug dumps via Salver trace events.
author Stefan Rumzucker <stefan.rumzucker@jku.at>
date Thu, 29 Oct 2015 17:49:30 +0100
parents 4baa8603ae7c
children 0bf0cdd17bd3
files .hgignore graal/com.oracle.graal.salver/src/com/oracle/graal/salver/Salver.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/SalverDebugConfigCustomizer.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/SalverOptions.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/data/DataDict.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/data/DataList.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/dumper/AbstractGraalDumper.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/dumper/AbstractMethodScopeDumper.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/dumper/AbstractSerializerDumper.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/dumper/Dumper.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/dumper/GraphDumper.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/handler/AbstractDumpHandler.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/handler/AbstractGraalDumpHandler.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/handler/DumpHandler.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/handler/GraphDumpHandler.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/package-info.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/serialize/AbstractSerializer.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/serialize/JSONSerializer.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/serialize/Serializer.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/util/ECIDUtil.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/util/MethodContext.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/writer/ChannelDumpWriter.java graal/com.oracle.graal.salver/src/com/oracle/graal/salver/writer/DumpWriter.java mx.graal/suite.py
diffstat 24 files changed, 1993 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Thu Oct 29 13:53:23 2015 +0100
+++ b/.hgignore	Thu Oct 29 17:49:30 2015 +0100
@@ -30,6 +30,7 @@
 \.dot$
 \.pyc$
 \.hprof$
+\.json$
 \javafilelist.*\.txt$
 \.hprof\.txt$
 ^doc/.*/dot_temp_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/Salver.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver;
+
+import static com.oracle.graal.salver.SalverOptions.SalverAddress;
+import static com.oracle.graal.salver.SalverOptions.SalverPort;
+
+import java.net.InetSocketAddress;
+
+import com.oracle.graal.salver.util.ECIDUtil;
+
+public final class Salver {
+
+    /**
+     * The Execution Context Identifier is a unique identifier that simplifies the grouping of
+     * events created in different DumpHandlers or Threads. It should be added as a special property
+     * to all :begin trace events.
+     */
+    public static final String ECID = ECIDUtil.random();
+
+    private Salver() {
+    }
+
+    public static InetSocketAddress getSocketAddress() {
+        return new InetSocketAddress(SalverAddress.getValue(), SalverPort.getValue());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/SalverDebugConfigCustomizer.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver;
+
+import static com.oracle.graal.salver.SalverOptions.Salver;
+
+import com.oracle.graal.debug.DebugConfig;
+import com.oracle.graal.debug.DebugConfigCustomizer;
+import com.oracle.graal.salver.handler.GraphDumpHandler;
+
+import jdk.vm.ci.service.ServiceProvider;
+
+@ServiceProvider(DebugConfigCustomizer.class)
+public class SalverDebugConfigCustomizer implements DebugConfigCustomizer {
+
+    public void customize(DebugConfig config) {
+        if (Salver.getValue()) {
+            config.dumpHandlers().add(new GraphDumpHandler());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/SalverOptions.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver;
+
+import jdk.vm.ci.options.Option;
+import jdk.vm.ci.options.OptionType;
+import jdk.vm.ci.options.OptionValue;
+
+public final class SalverOptions {
+
+    //@formatter:off
+    @Option(help = "Enable dumps via Salver trace events.", type = OptionType.Debug)
+    public static final OptionValue<Boolean> Salver = new OptionValue<>(false);
+
+    @Option(help = "Network address (Salver).", type = OptionType.Debug)
+    public static final OptionValue<String> SalverAddress = new OptionValue<>("127.0.0.1");
+
+    @Option(help = "Network port (Salver).", type = OptionType.Debug)
+    public static final OptionValue<Integer> SalverPort = new OptionValue<>(2343);
+
+    @Option(help = "Dump to files as opposed to sending them over the network (Salver).", type = OptionType.Debug)
+    public static final OptionValue<Boolean> SalverToFile = new OptionValue<>(false);
+
+    //@Option(help = "Use binary format for dumps (Salver).", type = OptionType.Debug)
+    //public static final OptionValue<Boolean> SalverDumpBinary = new OptionValue<>(false);
+    //@formatter:on
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/data/DataDict.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.data;
+
+import java.util.LinkedHashMap;
+
+public class DataDict extends LinkedHashMap<Object, Object> {
+
+    private static final long serialVersionUID = 1L;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/data/DataList.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.data;
+
+import java.util.ArrayList;
+
+public class DataList extends ArrayList<Object> {
+
+    private static final long serialVersionUID = 1L;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/dumper/AbstractGraalDumper.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.dumper;
+
+import java.io.IOException;
+
+import com.oracle.graal.salver.Salver;
+import com.oracle.graal.salver.data.DataDict;
+
+public class AbstractGraalDumper extends AbstractSerializerDumper {
+
+    public static final String EVENT_NAMESPACE = "graal";
+
+    private int eventCounter;
+
+    public void beginDump() throws IOException {
+        beginDump(EVENT_NAMESPACE);
+    }
+
+    protected void beginDump(String namespace) throws IOException {
+        beginDump(namespace, getBeginDumpDataDict());
+    }
+
+    protected void beginDump(String namespace, DataDict dataDict) throws IOException {
+        DataDict eventDict = createEventDict(":begin");
+        eventDict.put("@time", System.currentTimeMillis());
+        eventDict.put("@ecid", Salver.ECID);
+        if (namespace != null) {
+            eventDict.put("@namespace", namespace);
+        }
+        if (dataDict != null) {
+            eventDict.put("@data", dataDict);
+        }
+        serializeAndFlush(eventDict);
+    }
+
+    protected DataDict getBeginDumpDataDict() {
+        DataDict dataDict = new DataDict();
+        dataDict.put("dumper", getClass().getSimpleName());
+        dataDict.put("thread", Thread.currentThread().getName());
+        return dataDict;
+    }
+
+    public void endDump() throws IOException {
+        DataDict eventDict = createEventDict(":end");
+        eventDict.put("@time", System.currentTimeMillis());
+        serializeAndFlush(eventDict);
+    }
+
+    @Override
+    public void close() throws IOException {
+        endDump();
+    }
+
+    protected DataDict createEventDict(String name) {
+        DataDict eventDict = new DataDict();
+        eventDict.put("@event", name);
+        eventDict.put("@n", eventCounter++);
+        return eventDict;
+    }
+
+    protected DataDict createEventDict(String name, DataDict data) {
+        DataDict eventDict = createEventDict(name);
+        eventDict.put("@data", data);
+        return eventDict;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/dumper/AbstractMethodScopeDumper.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.dumper;
+
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+
+import com.oracle.graal.java.BytecodeDisassembler;
+import com.oracle.graal.salver.data.DataDict;
+import com.oracle.graal.salver.data.DataList;
+import com.oracle.graal.salver.util.MethodContext;
+
+import jdk.vm.ci.meta.JavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+
+public abstract class AbstractMethodScopeDumper extends AbstractGraalDumper {
+
+    protected MethodContext previousMethodContext;
+
+    protected final Deque<Integer> pathStack = new ArrayDeque<>();
+    protected int pathCounter;
+    protected final Deque<Integer> itemIdStack = new ArrayDeque<>();
+    protected int itemIdCounter;
+
+    protected void resolveMethodContext() throws IOException {
+        // Get all current JavaMethod instances in the context.
+        MethodContext methodContext = new MethodContext();
+        // Reverse list such that inner method comes after outer method.
+        Collections.reverse(methodContext);
+
+        int size = methodContext.size();
+        int previousSize = previousMethodContext != null ? previousMethodContext.size() : 0;
+        // Check for method scopes that must be closed since the previous dump.
+        for (int i = 0; i < previousSize; ++i) {
+            if (i >= size || !methodContext.itemEquals(i, previousMethodContext)) {
+                for (int inlineDepth = previousSize - 1; inlineDepth >= i; --inlineDepth) {
+                    closeScope();
+                }
+                break;
+            }
+        }
+        // Check for method scopes that must be opened since the previous dump.
+        for (int i = 0; i < size; ++i) {
+            if (i >= previousSize || !methodContext.itemEquals(i, previousMethodContext)) {
+                for (int inlineDepth = i; inlineDepth < size; ++inlineDepth) {
+                    openScope(methodContext.get(inlineDepth));
+                }
+                break;
+            }
+        }
+        // Save inline context for next dump.
+        previousMethodContext = methodContext;
+    }
+
+    protected void openScope(MethodContext.Item item) throws IOException {
+        int debugId = item.getDebugId();
+        pathStack.push(debugId != -1 ? debugId : pathCounter);
+        itemIdStack.push(itemIdCounter);
+        pathCounter = 0;
+        itemIdCounter = 0;
+
+        processMethod(item.getMethod(), item.getName());
+    }
+
+    @SuppressWarnings("unused")
+    protected void closeScope() throws IOException {
+        if (!pathStack.isEmpty()) {
+            pathCounter = pathStack.pop();
+            pathCounter++;
+        }
+        if (!itemIdStack.isEmpty()) {
+            itemIdCounter = itemIdStack.pop();
+        }
+    }
+
+    protected void processMethod(JavaMethod method, String name) throws IOException {
+        DataDict dataDict = new DataDict();
+        dataDict.put("name", name);
+
+        if (method instanceof ResolvedJavaMethod) {
+            DataDict methodDict = new DataDict();
+            dataDict.put("method", methodDict);
+
+            ResolvedJavaMethod resolvedMethod = (ResolvedJavaMethod) method;
+
+            methodDict.put("modifiers", Modifier.toString(resolvedMethod.getModifiers()));
+            methodDict.put("code", new BytecodeDisassembler(false).disassemble(resolvedMethod));
+        }
+        serializeAndFlush(createEventDictWithId("method", dataDict, false));
+    }
+
+    protected int nextItemId() {
+        return itemIdCounter++;
+    }
+
+    protected DataDict createEventDictWithId(String name, boolean isItem) {
+        DataDict eventDict = createEventDict(name);
+
+        DataDict idDict = new DataDict();
+        eventDict.put("@id", idDict);
+
+        DataList pathList = new DataList();
+        idDict.put("path", pathList);
+
+        for (int i : pathStack) {
+            pathList.add(i);
+        }
+        if (isItem) {
+            pathList.add(pathCounter++);
+        }
+        return eventDict;
+    }
+
+    protected DataDict createEventDictWithId(String name, DataDict dataDict, boolean isItem) {
+        DataDict eventDict = createEventDictWithId(name, isItem);
+        eventDict.put("@data", dataDict);
+        return eventDict;
+    }
+
+    protected DataDict createEventDictWithId(String name) {
+        return createEventDictWithId(name, true);
+    }
+
+    protected DataDict createEventDictWithId(String name, DataDict dataDict) {
+        return createEventDictWithId(name, dataDict, true);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/dumper/AbstractSerializerDumper.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.dumper;
+
+import java.io.IOException;
+
+import com.oracle.graal.salver.serialize.Serializer;
+
+public abstract class AbstractSerializerDumper implements Dumper {
+
+    protected Serializer serializer;
+
+    public AbstractSerializerDumper() {
+    }
+
+    public AbstractSerializerDumper(Serializer serializer) {
+        this.serializer = serializer;
+    }
+
+    public Serializer getSerializer() {
+        return serializer;
+    }
+
+    public void setSerializer(Serializer serializer) {
+        this.serializer = serializer;
+    }
+
+    protected void serialize(Object obj) throws IOException {
+        if (serializer != null) {
+            serializer.serialize(obj);
+        }
+    }
+
+    protected void serializeAndFlush(Object obj) throws IOException {
+        if (serializer != null) {
+            serializer.serialize(obj);
+            serializer.flush();
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/dumper/Dumper.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.dumper;
+
+import java.io.Closeable;
+
+public interface Dumper extends Closeable, AutoCloseable {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/dumper/GraphDumper.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.dumper;
+
+import static com.oracle.graal.compiler.common.GraalOptions.PrintGraphProbabilities;
+import static com.oracle.graal.compiler.common.GraalOptions.PrintIdealGraphSchedule;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.oracle.graal.compiler.common.Fields;
+import com.oracle.graal.compiler.common.cfg.BlockMap;
+import com.oracle.graal.debug.Debug;
+import com.oracle.graal.debug.Debug.Scope;
+import com.oracle.graal.graph.Edges;
+import com.oracle.graal.graph.Edges.Type;
+import com.oracle.graal.graph.Graph;
+import com.oracle.graal.graph.InputEdges;
+import com.oracle.graal.graph.Node;
+import com.oracle.graal.graph.NodeClass;
+import com.oracle.graal.graph.NodeList;
+import com.oracle.graal.graph.NodeMap;
+import com.oracle.graal.graph.iterators.NodeIterable;
+import com.oracle.graal.nodes.AbstractBeginNode;
+import com.oracle.graal.nodes.AbstractEndNode;
+import com.oracle.graal.nodes.AbstractMergeNode;
+import com.oracle.graal.nodes.ControlSinkNode;
+import com.oracle.graal.nodes.ControlSplitNode;
+import com.oracle.graal.nodes.FixedNode;
+import com.oracle.graal.nodes.PhiNode;
+import com.oracle.graal.nodes.ProxyNode;
+import com.oracle.graal.nodes.StructuredGraph;
+import com.oracle.graal.nodes.VirtualState;
+import com.oracle.graal.nodes.cfg.Block;
+import com.oracle.graal.nodes.cfg.ControlFlowGraph;
+import com.oracle.graal.phases.schedule.SchedulePhase;
+import com.oracle.graal.salver.data.DataDict;
+import com.oracle.graal.salver.data.DataList;
+
+public class GraphDumper extends AbstractMethodScopeDumper {
+
+    public static final String EVENT_NAMESPACE = "graal/graph";
+
+    private static final Map<Class<?>, String> nodeClassCategoryMap;
+
+    static {
+        nodeClassCategoryMap = new LinkedHashMap<>();
+        nodeClassCategoryMap.put(ControlSinkNode.class, "ControlSink");
+        nodeClassCategoryMap.put(ControlSplitNode.class, "ControlSplit");
+        nodeClassCategoryMap.put(AbstractMergeNode.class, "Merge");
+        nodeClassCategoryMap.put(AbstractBeginNode.class, "Begin");
+        nodeClassCategoryMap.put(AbstractEndNode.class, "End");
+        nodeClassCategoryMap.put(FixedNode.class, "Fixed");
+        nodeClassCategoryMap.put(VirtualState.class, "State");
+        nodeClassCategoryMap.put(PhiNode.class, "Phi");
+        nodeClassCategoryMap.put(ProxyNode.class, "Proxy");
+        // nodeClassCategoryMap.put(Object.class, "Floating");
+    }
+
+    @Override
+    public void beginDump() throws IOException {
+        beginDump(EVENT_NAMESPACE);
+    }
+
+    @SuppressWarnings("try")
+    public void dump(Graph graph, String msg) throws IOException {
+        resolveMethodContext();
+
+        try (Scope s = Debug.sandbox(getClass().getSimpleName(), null)) {
+            SchedulePhase predefinedSchedule = null;
+            for (Object obj : Debug.context()) {
+                if (obj instanceof SchedulePhase) {
+                    predefinedSchedule = (SchedulePhase) obj;
+                }
+            }
+            processGraph(graph, msg, predefinedSchedule);
+        } catch (IOException e) {
+            throw e;
+        } catch (Throwable e) {
+            throw Debug.handle(e);
+        }
+    }
+
+    private void processGraph(Graph graph, String name, SchedulePhase predefinedSchedule) throws IOException {
+        SchedulePhase schedule = predefinedSchedule;
+        if (schedule == null) {
+            // Also provide a schedule when an error occurs
+            if (PrintIdealGraphSchedule.getValue() || Debug.contextLookup(Throwable.class) != null) {
+                if (graph instanceof StructuredGraph) {
+                    schedule = new SchedulePhase();
+                    schedule.apply((StructuredGraph) graph);
+                }
+            }
+        }
+
+        ControlFlowGraph cfg = null;
+        List<Block> blocks = null;
+        NodeMap<Block> nodeToBlock = null;
+        BlockMap<List<Node>> blockToNodes = null;
+
+        if (schedule != null) {
+            cfg = schedule.getCFG();
+            if (cfg != null) {
+                blocks = cfg.getBlocks();
+                nodeToBlock = schedule.getNodeToBlockMap();
+                blockToNodes = schedule.getBlockToNodesMap();
+            }
+        }
+
+        DataDict dataDict = new DataDict();
+        dataDict.put("id", nextItemId());
+        dataDict.put("name", name);
+
+        DataDict graphDict = new DataDict();
+        dataDict.put("graph", graphDict);
+
+        processNodes(graphDict, graph.getNodes(), nodeToBlock, cfg);
+
+        if (blocks != null && blockToNodes != null) {
+            processBlocks(graphDict, blocks, blockToNodes);
+        }
+        serializeAndFlush(createEventDictWithId("graph", dataDict));
+    }
+
+    private static void processNodes(DataDict graphDict, NodeIterable<Node> nodes, NodeMap<Block> nodeToBlock, ControlFlowGraph cfg) {
+        Map<NodeClass<?>, Integer> classMap = new HashMap<>();
+
+        DataList classList = new DataList();
+        graphDict.put("classes", classList);
+
+        DataList nodeList = new DataList();
+        graphDict.put("nodes", nodeList);
+
+        DataList edgeList = new DataList();
+        graphDict.put("edges", edgeList);
+
+        for (Node node : nodes) {
+            NodeClass<?> nodeClass = node.getNodeClass();
+
+            DataDict nodeDict = new DataDict();
+            nodeDict.put("id", getNodeId(node));
+            nodeDict.put("class", getNodeClassId(classMap, classList, nodeClass));
+
+            if (nodeToBlock != null) {
+                if (nodeToBlock.isNew(node)) {
+                    nodeDict.put("block", -1);
+                } else {
+                    Block block = nodeToBlock.get(node);
+                    if (block != null) {
+                        nodeDict.put("block", block.getId());
+                    }
+                }
+            }
+
+            if (cfg != null && PrintGraphProbabilities.getValue() && node instanceof FixedNode) {
+                try {
+                    nodeDict.put("probability", cfg.blockFor(node).probability());
+                } catch (Throwable t) {
+                    nodeDict.put("probability", t);
+                }
+            }
+
+            Map<Object, Object> debugProperties = node.getDebugProperties();
+            if (!debugProperties.isEmpty()) {
+                DataDict propertyDict = new DataDict();
+                nodeDict.put("properties", propertyDict);
+                for (Map.Entry<Object, Object> entry : debugProperties.entrySet()) {
+                    propertyDict.put(entry.getKey().toString(), entry.getValue());
+                }
+            }
+
+            nodeList.add(nodeDict);
+            appendEdges(edgeList, node, Type.Inputs);
+            appendEdges(edgeList, node, Type.Successors);
+        }
+    }
+
+    private static void processBlocks(DataDict graphDict, List<Block> blocks, BlockMap<List<Node>> blockToNodes) {
+        DataList blockList = new DataList();
+        graphDict.put("blocks", blockList);
+
+        for (Block block : blocks) {
+            List<Node> nodes = blockToNodes.get(block);
+            if (nodes != null) {
+                DataDict blockDict = new DataDict();
+                blockDict.put("id", block.getId());
+
+                DataList nodeList = new DataList();
+                blockDict.put("nodes", nodeList);
+
+                for (Node node : nodes) {
+                    nodeList.add(getNodeId(node));
+                }
+
+                DataList successorList = new DataList();
+                blockDict.put("successors", successorList);
+                for (Block successor : block.getSuccessors()) {
+                    successorList.add(successor.getId());
+                }
+
+                blockList.add(blockDict);
+            }
+        }
+    }
+
+    private static void appendEdges(DataList edgeList, Node node, Edges.Type type) {
+        NodeClass<?> nodeClass = node.getNodeClass();
+
+        Edges edges = nodeClass.getEdges(type);
+        final long[] curOffsets = edges.getOffsets();
+
+        for (int i = 0; i < edges.getDirectCount(); i++) {
+            Node other = Edges.getNode(node, curOffsets, i);
+            if (other != null) {
+                DataDict edgeDict = new DataDict();
+
+                DataDict nodeDict = new DataDict();
+                nodeDict.put("node", getNodeId(node));
+                nodeDict.put("field", edges.getName(i));
+
+                edgeDict.put("from", type == Type.Inputs ? getNodeId(other) : nodeDict);
+                edgeDict.put("to", type == Type.Inputs ? nodeDict : getNodeId(other));
+                edgeList.add(edgeDict);
+            }
+        }
+        for (int i = edges.getDirectCount(); i < edges.getCount(); i++) {
+            NodeList<Node> list = Edges.getNodeList(node, curOffsets, i);
+            if (list != null) {
+                for (int index = 0; index < list.size(); index++) {
+                    Node other = list.get(index);
+                    if (other != null) {
+                        DataDict edgeDict = new DataDict();
+
+                        DataDict nodeDict = new DataDict();
+                        nodeDict.put("node", getNodeId(node));
+                        nodeDict.put("field", edges.getName(i));
+                        nodeDict.put("index", index);
+
+                        edgeDict.put("from", type == Type.Inputs ? getNodeId(other) : nodeDict);
+                        edgeDict.put("to", type == Type.Inputs ? nodeDict : getNodeId(other));
+                        edgeList.add(edgeDict);
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private static int getNodeId(Node node) {
+        return node != null ? node.getId() : -1;
+    }
+
+    private static int getNodeClassId(Map<NodeClass<?>, Integer> classMap, DataList classList, NodeClass<?> nodeClass) {
+        if (classMap.containsKey(nodeClass)) {
+            return classMap.get(nodeClass);
+        }
+        int classId = classMap.size();
+        classMap.put(nodeClass, classId);
+
+        Class<?> javaClass = nodeClass.getJavaClass();
+
+        DataDict classDict = new DataDict();
+        classList.add(classDict);
+
+        classDict.put("id", classId);
+        classDict.put("name", nodeClass.getNameTemplate());
+        classDict.put("jtype", javaClass.getName());
+
+        String category = getNodeClassCategory(javaClass);
+        if (category != null) {
+            classDict.put("category", category);
+        }
+
+        Object propertyInfo = getPropertyInfo(nodeClass);
+        if (propertyInfo != null) {
+            classDict.put("properties", propertyInfo);
+        }
+
+        Object inputInfo = getEdgeInfo(nodeClass, Type.Inputs);
+        if (inputInfo != null) {
+            classDict.put("inputs", inputInfo);
+        }
+        Object successorInfo = getEdgeInfo(nodeClass, Type.Successors);
+        if (successorInfo != null) {
+            classDict.put("successors", successorInfo);
+        }
+        return classId;
+    }
+
+    private static DataDict getPropertyInfo(NodeClass<?> nodeClass) {
+        Fields properties = nodeClass.getData();
+        if (properties.getCount() > 0) {
+            DataDict propertyInfoDict = new DataDict();
+            for (int i = 0; i < properties.getCount(); i++) {
+                DataDict propertyDict = new DataDict();
+                String name = properties.getName(i);
+                propertyDict.put("name", name);
+                propertyDict.put("jtype", properties.getType(i).getName());
+                propertyInfoDict.put(name, propertyDict);
+            }
+            return propertyInfoDict;
+        }
+        return null;
+    }
+
+    private static DataDict getEdgeInfo(NodeClass<?> nodeClass, Edges.Type type) {
+        DataDict edgeInfoDict = new DataDict();
+        Edges edges = nodeClass.getEdges(type);
+        for (int i = 0; i < edges.getCount(); i++) {
+            DataDict edgeDict = new DataDict();
+            String name = edges.getName(i);
+            edgeDict.put("name", name);
+            edgeDict.put("jtype", edges.getType(i).getName());
+            if (type == Type.Inputs) {
+                edgeDict.put("type", ((InputEdges) edges).getInputType(i));
+            }
+            edgeInfoDict.put(name, edgeDict);
+        }
+        return edgeInfoDict.isEmpty() ? null : edgeInfoDict;
+    }
+
+    private static String getNodeClassCategory(Class<?> clazz) {
+        for (Map.Entry<Class<?>, String> entry : nodeClassCategoryMap.entrySet()) {
+            if (entry.getKey().isAssignableFrom(clazz)) {
+                return entry.getValue();
+            }
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/handler/AbstractDumpHandler.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.handler;
+
+import static java.nio.file.StandardOpenOption.CREATE;
+import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
+import static java.nio.file.StandardOpenOption.WRITE;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.nio.channels.FileChannel;
+import java.nio.channels.SocketChannel;
+import java.nio.channels.WritableByteChannel;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+
+import com.oracle.graal.salver.dumper.Dumper;
+import com.oracle.graal.salver.writer.ChannelDumpWriter;
+import com.oracle.graal.salver.writer.DumpWriter;
+
+public abstract class AbstractDumpHandler<D extends Dumper> implements DumpHandler {
+
+    protected String label;
+
+    protected DumpWriter writer;
+    protected D dumper;
+
+    public AbstractDumpHandler() {
+        setLabel(getClass().getSimpleName() + ":" + Thread.currentThread().getName());
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    protected void setLabel(String label) {
+        this.label = label;
+    }
+
+    public DumpWriter getWriter() {
+        return writer;
+    }
+
+    protected void setWriter(DumpWriter writer) {
+        this.writer = writer;
+    }
+
+    protected void setWriter(WritableByteChannel channel) {
+        setWriter(new ChannelDumpWriter(channel));
+    }
+
+    protected void setWriter(SocketAddress remote) throws IOException {
+        setWriter(SocketChannel.open(remote));
+    }
+
+    protected void setWriter(Path path) throws IOException {
+        setWriter(path, WRITE, TRUNCATE_EXISTING, CREATE);
+    }
+
+    protected void setWriter(Path path, OpenOption... options) throws IOException {
+        setWriter(FileChannel.open(path, options));
+    }
+
+    public D getDumper() {
+        return dumper;
+    }
+
+    protected void setDumper(D dumper) {
+        this.dumper = dumper;
+    }
+
+    public void close() throws IOException {
+        if (dumper != null) {
+            try {
+                dumper.close();
+            } finally {
+                dumper = null;
+            }
+        }
+        if (writer != null) {
+            try {
+                writer.close();
+            } finally {
+                writer = null;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/handler/AbstractGraalDumpHandler.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.handler;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.channels.ClosedByInterruptException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.oracle.graal.debug.DebugDumpHandler;
+import com.oracle.graal.debug.TTY;
+import com.oracle.graal.salver.Salver;
+import com.oracle.graal.salver.SalverOptions;
+import com.oracle.graal.salver.dumper.AbstractGraalDumper;
+import com.oracle.graal.salver.serialize.JSONSerializer;
+import com.oracle.graal.salver.serialize.Serializer;
+
+public abstract class AbstractGraalDumpHandler<D extends AbstractGraalDumper> extends AbstractDumpHandler<D> implements DebugDumpHandler {
+
+    private Serializer serializer;
+
+    private static final int MAX_FAILURES = 7;
+    private int failures;
+
+    public static final class NotInitializedException extends IOException {
+
+        private static final long serialVersionUID = 1L;
+    }
+
+    protected void ensureInitialized() throws IOException {
+        if (writer == null) {
+            if (failures < MAX_FAILURES) {
+                if (SalverOptions.SalverToFile.getValue()) {
+                    initializeFileChannelWriter();
+                } else {
+                    initializeSocketChannelWriter();
+                }
+            }
+            if (writer == null) {
+                throw new NotInitializedException();
+            }
+        }
+        if (dumper == null) {
+            dumper = createDumper();
+            if (dumper == null) {
+                throw new NotInitializedException();
+            }
+            if (serializer == null) {
+                serializer = createSerializer();
+            }
+            if (serializer.getWriter() != writer) {
+                serializer.setWriter(writer);
+            }
+            dumper.setSerializer(serializer);
+            dumper.beginDump();
+        }
+    }
+
+    protected abstract D createDumper();
+
+    protected Serializer createSerializer() {
+        return new JSONSerializer();
+    }
+
+    protected abstract void handle(Object obj, String msg) throws IOException;
+
+    protected void initializeSocketChannelWriter() {
+        InetSocketAddress socketAddress = Salver.getSocketAddress();
+        try {
+            setWriter(socketAddress);
+            printlnTTY("Connected to %s:%d (ECID = %s)", socketAddress.getHostName(), socketAddress.getPort(), Salver.ECID);
+        } catch (ClosedByInterruptException e) {
+            // May be caused by a cancelled Graal compilation
+        } catch (IOException e) {
+            printlnTTY("Couldn't connect to %s:%d (%s)", socketAddress.getHostName(), socketAddress.getPort(), e);
+            failures++;
+        }
+    }
+
+    private static final ThreadLocal<SimpleDateFormat> sdf = new ThreadLocal<SimpleDateFormat>() {
+        @Override
+        protected SimpleDateFormat initialValue() {
+            return new SimpleDateFormat("YYYY-MM-dd_HH-mm");
+        }
+    };
+
+    protected void initializeFileChannelWriter() {
+        String filename = sdf.get().format(new Date());
+        if (label != null) {
+            filename += "_" + Salver.ECID + "_" + label.replaceAll("(?i)[^a-z0-9-]", "-");
+        }
+        String fileExt = JSONSerializer.getFileExtension();
+        File file = new File(filename + "." + fileExt);
+        try {
+            for (int i = 1; file.exists(); i++) {
+                if (i < 1 << 7) {
+                    file = new File(filename + "_" + i + "." + fileExt);
+                } else {
+                    throw new IOException();
+                }
+            }
+            setWriter(file.toPath());
+            printlnTTY("Dumping to \"%s\"", file.getName());
+        } catch (ClosedByInterruptException e) {
+            // May be caused by a cancelled Graal compilation
+        } catch (IOException e) {
+            printlnTTY("Failed to open %s for dumping (%s)", file.getName(), e);
+            failures++;
+        }
+    }
+
+    public void dump(Object obj) {
+        dump(obj, null);
+    }
+
+    @Override
+    public void dump(Object obj, String msg) {
+        try {
+            handle(obj, msg);
+        } catch (NotInitializedException e) {
+            // Ignore
+        } catch (IOException e) {
+            printlnTTY("%s", e);
+            if (failures < MAX_FAILURES) {
+                failures++;
+            } else {
+                close();
+            }
+        }
+    }
+
+    @Override
+    public void close() {
+        try {
+            super.close();
+        } catch (IOException e) {
+            printlnTTY("%s", e);
+        } finally {
+            failures = 0;
+        }
+    }
+
+    protected void printlnTTY(String format, Object... args) {
+        if (label != null) {
+            TTY.println("[" + label + "] " + format, args);
+        } else {
+            TTY.println(format, args);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/handler/DumpHandler.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.handler;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+public interface DumpHandler extends Closeable, AutoCloseable {
+
+    void dump(Object obj, String msg) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/handler/GraphDumpHandler.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.handler;
+
+import java.io.IOException;
+
+import com.oracle.graal.graph.Graph;
+import com.oracle.graal.salver.dumper.GraphDumper;
+
+public class GraphDumpHandler extends AbstractGraalDumpHandler<GraphDumper> {
+
+    @Override
+    protected GraphDumper createDumper() {
+        return new GraphDumper();
+    }
+
+    @Override
+    public void handle(Object obj, String msg) throws IOException {
+        if (obj instanceof Graph) {
+            ensureInitialized();
+            dumper.dump((Graph) obj, msg);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/package-info.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/**
+ * <p>
+ *   This package provides the basic functionality to send debug dumps to a
+ *   server that is able to process Salver trace events.
+ * </p>
+ *
+ * <p>
+ *   Salver is a Web Application Framework for Trace Visualization which
+ *   defines an interface for sending trace information via serializable events,
+ *   e.g. as JSON text representation. Events are grouped into separate
+ *   namespaces to simplify event processing. Listeners can register for a
+ *   certain namespace and will receive all relevant events.
+ * </p>
+ *
+ * <p>
+ *   In order to process trace events of this package the server needs to listen
+ *   for the "graal/graph" namespace.
+ * </p>
+ *
+ * <p>
+ *   See {@link com.oracle.graal.salver.SalverOptions SalverOptions} for all
+ *   available options.
+ * <p>
+ *
+ * <p>
+ *   Basic components of this package:
+ *   <ul>
+ *     <li>DumpHandler:<br/>
+ *       Initialization of the corresponding Dumper in case a given object can
+ *       be processed.
+ *     </li>
+ *     <li>Dumper:<br/>
+ *       Processing the object about to be dumped and extract all necessary
+ *       information in order to produce a serializable data object.
+ *     </li>
+ *     <li>Serializer:<br/>
+ *       Serializing a given data object, e.g. to a JSON text representation.
+ *     </li>
+ *     <li>Writer:<br/>
+ *       Writing the actual data, e.g. to a WritableByteChannel.
+ *     </li>
+ *   </ul>
+ * </p>
+ *
+ * <p>
+ *   Currently available {@link com.oracle.graal.debug.DebugDumpHandler DebugDumpHandlers}:
+ *   <ul>
+ *     <li>{@link com.oracle.graal.salver.handler.GraphDumpHandler GraphDumpHandler}:<br>
+ *       Dumps {@link com.oracle.graal.graph.Graph Graphs} (extracts almost the
+ *       same information as IGV's dump handler).
+ *     </li>
+ *   </ul>
+ * </p>
+ */
+package com.oracle.graal.salver;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/serialize/AbstractSerializer.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.serialize;
+
+import java.io.IOException;
+
+import com.oracle.graal.salver.writer.DumpWriter;
+
+public abstract class AbstractSerializer implements Serializer {
+
+    protected DumpWriter writer;
+
+    public AbstractSerializer() {
+    }
+
+    public AbstractSerializer(DumpWriter writer) {
+        if (writer != null) {
+            setWriter(writer);
+        }
+    }
+
+    public DumpWriter getWriter() {
+        return writer;
+    }
+
+    public void setWriter(DumpWriter writer) {
+        this.writer = writer;
+    }
+
+    public void reset() throws IOException {
+    }
+
+    public void flush() throws IOException {
+        if (writer != null) {
+            writer.flush();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/serialize/JSONSerializer.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.serialize;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import com.oracle.graal.salver.writer.DumpWriter;
+
+public class JSONSerializer extends AbstractSerializer {
+
+    public static final String MEDIA_TYPE = "application/json";
+    public static final String FILE_EXTENSION = "json";
+
+    public JSONSerializer() {
+    }
+
+    public JSONSerializer(DumpWriter writer) {
+        super(writer);
+    }
+
+    public JSONSerializer serialize(Object obj) throws IOException {
+        if (writer != null) {
+            writer.write(appendValue(new StringBuilder(), obj).append('\n'));
+        }
+        return this;
+    }
+
+    public static StringBuilder stringify(StringBuilder sb, Object obj) {
+        return appendValue(sb, obj);
+    }
+
+    public static String stringify(Object obj) {
+        return appendValue(new StringBuilder(), obj).toString();
+    }
+
+    public static String getMediaType() {
+        return MEDIA_TYPE;
+    }
+
+    public static String getFileExtension() {
+        return FILE_EXTENSION;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static StringBuilder appendValue(StringBuilder sb, Object val) {
+        if (val instanceof Map<?, ?>) {
+            return appendDict(sb, (Map<Object, Object>) val);
+        }
+        if (val instanceof List<?>) {
+            return appendList(sb, (List<Object>) val);
+        }
+        if (val instanceof byte[]) {
+            return appendByteArray(sb, (byte[]) val);
+        }
+        if (val instanceof Number) {
+            return sb.append(val);
+        }
+        if (val instanceof Boolean) {
+            return sb.append(val);
+        }
+        if (val == null) {
+            return sb.append("null");
+        }
+        return appendString(sb, String.valueOf(val));
+    }
+
+    private static StringBuilder appendDict(StringBuilder sb, Map<Object, Object> dict) {
+        sb.append('{');
+        boolean comma = false;
+        for (Map.Entry<Object, Object> entry : dict.entrySet()) {
+            if (comma) {
+                sb.append(',');
+            } else {
+                comma = true;
+            }
+            appendString(sb, String.valueOf(entry.getKey()));
+            sb.append(':');
+            appendValue(sb, entry.getValue());
+        }
+        return sb.append('}');
+    }
+
+    private static StringBuilder appendList(StringBuilder sb, List<Object> list) {
+        sb.append('[');
+        boolean comma = false;
+        for (Object val : list) {
+            if (comma) {
+                sb.append(',');
+            } else {
+                comma = true;
+            }
+            appendValue(sb, val);
+        }
+        return sb.append(']');
+    }
+
+    private static StringBuilder appendString(StringBuilder sb, String str) {
+        sb.append('"');
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            switch (c) {
+                case '"':
+                    sb.append("\\\"");
+                    break;
+                case '\\':
+                    sb.append("\\\\");
+                    break;
+                case '\b':
+                    sb.append("\\b");
+                    break;
+                case '\f':
+                    sb.append("\\f");
+                    break;
+                case '\n':
+                    sb.append("\\n");
+                    break;
+                case '\r':
+                    sb.append("\\r");
+                    break;
+                case '\t':
+                    sb.append("\\t");
+                    break;
+                default: {
+                    if (Character.isISOControl(c)) {
+                        sb.append("\\u00");
+                        sb.append(Character.forDigit((c >> 4) & 0xF, 16));
+                        sb.append(Character.forDigit(c & 0xF, 16));
+                    } else {
+                        sb.append(c);
+                    }
+                }
+            }
+        }
+        return sb.append('"');
+    }
+
+    private static StringBuilder appendByteArray(StringBuilder sb, byte[] arr) {
+        if (arr.length > 0) {
+            sb.append("0x");
+            for (byte b : arr) {
+                sb.append(String.format("%02x", b));
+            }
+            return sb;
+        }
+        return sb.append("null");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/serialize/Serializer.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.serialize;
+
+import java.io.Flushable;
+import java.io.IOException;
+
+import com.oracle.graal.salver.writer.DumpWriter;
+
+public interface Serializer extends Flushable {
+
+    DumpWriter getWriter();
+
+    void setWriter(DumpWriter writer);
+
+    Serializer serialize(Object obj) throws IOException;
+
+    void reset() throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/util/ECIDUtil.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.util;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+public class ECIDUtil {
+
+    private static final Random RANDOM = new SecureRandom();
+
+    private static final String BASE58_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz123456789";
+
+    public static String random() {
+        return random(8);
+    }
+
+    public static String random(int length) {
+        StringBuilder sb = new StringBuilder();
+        byte[] bytes = new byte[length];
+        RANDOM.nextBytes(bytes);
+        for (byte b : bytes) {
+            sb.append(BASE58_ALPHABET.charAt((b & 0xff) % 58));
+        }
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/util/MethodContext.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.util;
+
+import static com.oracle.graal.debug.GraalDebugConfig.asJavaMethod;
+
+import java.util.ArrayList;
+
+import com.oracle.graal.debug.Debug;
+import com.oracle.graal.debug.DebugDumpScope;
+import com.oracle.graal.graph.Graph;
+import com.oracle.graal.salver.util.MethodContext.Item;
+
+import jdk.vm.ci.meta.JavaMethod;
+
+public class MethodContext extends ArrayList<Item> {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final class Item {
+
+        private String name;
+        private JavaMethod method;
+        private int debugId;
+
+        private Item(String name, JavaMethod method, int debugId) {
+            this.name = name;
+            this.method = method;
+            this.debugId = debugId;
+        }
+
+        private Item(JavaMethod method) {
+            this(method.format("%H::%n(%p)"), method, -1);
+        }
+
+        private Item(String name) {
+            this(name, null, -1);
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public JavaMethod getMethod() {
+            return method;
+        }
+
+        public int getDebugId() {
+            return debugId;
+        }
+    }
+
+    public MethodContext() {
+        Object lastMethodOrGraph = null;
+        for (Object obj : Debug.context()) {
+            JavaMethod method = asJavaMethod(obj);
+            if (method != null) {
+                JavaMethod lastAsMethod = asJavaMethod(lastMethodOrGraph);
+                if (lastAsMethod == null || !lastAsMethod.equals(method)) {
+                    add(new Item(method));
+                } else {
+                    /*
+                     * This prevents multiple adjacent method context objects for the same method
+                     * from resulting in multiple IGV tree levels. This works on the assumption that
+                     * real inlining debug scopes will have a graph context object between the
+                     * inliner and inlinee context objects.
+                     */
+                }
+            } else if (obj instanceof DebugDumpScope) {
+                DebugDumpScope debugDumpScope = (DebugDumpScope) obj;
+                if (debugDumpScope.decorator && !isEmpty()) {
+                    try {
+                        get(size() - 1).debugId = Integer.parseInt(debugDumpScope.name);
+                    } catch (NumberFormatException e) {
+                        // Ignore
+                    }
+                } else {
+                    add(new Item(debugDumpScope.name));
+                }
+            }
+            if (obj instanceof JavaMethod || obj instanceof Graph) {
+                lastMethodOrGraph = obj;
+            }
+        }
+        if (isEmpty()) {
+            add(new Item("Top Scope"));
+        }
+    }
+
+    public boolean itemEquals(int index, MethodContext context) {
+        Item i1 = get(index);
+        Item i2 = context != null ? context.get(index) : null;
+        if (i1 != null && i2 != null && i1.name != null && i2.name != null) {
+            return i1.name.equals(i2.name) && i1.debugId == i2.debugId;
+        }
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/writer/ChannelDumpWriter.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.writer;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.ReadOnlyBufferException;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.StandardCharsets;
+
+public class ChannelDumpWriter implements DumpWriter {
+
+    private static final int BUFFER_CAPACITY = 256 * 1024;
+
+    protected final WritableByteChannel channel;
+    protected final ByteBuffer buffer;
+
+    public ChannelDumpWriter(WritableByteChannel channel) {
+        this(channel, ByteBuffer.allocateDirect(BUFFER_CAPACITY));
+    }
+
+    public ChannelDumpWriter(WritableByteChannel channel, ByteBuffer buffer) {
+        this.channel = channel;
+        this.buffer = buffer;
+    }
+
+    private void ensureAvailable(int len) throws IOException {
+        if (buffer.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+        if (buffer.capacity() < len) {
+            throw new IllegalArgumentException();
+        }
+        while (buffer.remaining() < len) {
+            flush();
+        }
+    }
+
+    public ChannelDumpWriter write(byte b) throws IOException {
+        ensureAvailable(1);
+        buffer.put(b);
+        return this;
+    }
+
+    public ChannelDumpWriter write(byte[] arr) throws IOException {
+        if (buffer.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+        int offset = 0;
+        while (offset < arr.length) {
+            int available = buffer.remaining();
+            int length = Math.min(available, arr.length - offset);
+            buffer.put(arr, offset, length);
+            if (!buffer.hasRemaining()) {
+                flush();
+            }
+            offset += length;
+        }
+        return this;
+    }
+
+    public ChannelDumpWriter write(ByteBuffer buf) throws IOException {
+        if (buf == buffer) {
+            throw new IllegalArgumentException();
+        }
+        if (buffer.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+        while (buf.hasRemaining()) {
+            int available = buffer.remaining();
+            int remaining = buf.remaining();
+            for (int i = 0, n = Math.min(available, remaining); i < n; i++) {
+                buffer.put(buf.get());
+            }
+            if (!buffer.hasRemaining()) {
+                flush();
+            }
+        }
+        return this;
+    }
+
+    public ChannelDumpWriter write(CharSequence csq) throws IOException {
+        if (buffer.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+        CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
+        CharBuffer buf = CharBuffer.wrap(csq);
+        while (true) {
+            CoderResult result = encoder.encode(buf, buffer, true);
+            if (result.isError()) {
+                throw new IOException(result.toString());
+            }
+            if (!buffer.hasRemaining()) {
+                flush();
+            }
+            if (result.isOverflow()) {
+                continue;
+            }
+            break;
+        }
+        return this;
+    }
+
+    public ChannelDumpWriter writeChar(char v) throws IOException {
+        ensureAvailable(1 << 1);
+        buffer.putChar(v);
+        return this;
+    }
+
+    public ChannelDumpWriter writeShort(short v) throws IOException {
+        ensureAvailable(1 << 1);
+        buffer.putShort(v);
+        return this;
+    }
+
+    public ChannelDumpWriter writeInt(int v) throws IOException {
+        ensureAvailable(1 << 2);
+        buffer.putInt(v);
+        return this;
+    }
+
+    public ChannelDumpWriter writeLong(long v) throws IOException {
+        ensureAvailable(1 << 3);
+        buffer.putLong(v);
+        return this;
+    }
+
+    public ChannelDumpWriter writeFloat(float v) throws IOException {
+        ensureAvailable(1 << 2);
+        buffer.putFloat(v);
+        return this;
+    }
+
+    public ChannelDumpWriter writeDouble(double v) throws IOException {
+        ensureAvailable(1 << 3);
+        buffer.putDouble(v);
+        return this;
+    }
+
+    public void flush() throws IOException {
+        if (buffer != null && channel != null) {
+            buffer.flip();
+            channel.write(buffer);
+            buffer.compact();
+        }
+    }
+
+    public void close() throws IOException {
+        if (channel != null) {
+            try {
+                flush();
+            } finally {
+                channel.close();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.salver/src/com/oracle/graal/salver/writer/DumpWriter.java	Thu Oct 29 17:49:30 2015 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.salver.writer;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public interface DumpWriter extends Closeable, Flushable, AutoCloseable {
+
+    DumpWriter write(byte b) throws IOException;
+
+    DumpWriter write(byte[] arr) throws IOException;
+
+    DumpWriter write(ByteBuffer buf) throws IOException;
+
+    DumpWriter write(CharSequence csq) throws IOException;
+
+    DumpWriter writeChar(char v) throws IOException;
+
+    DumpWriter writeShort(short v) throws IOException;
+
+    DumpWriter writeInt(int v) throws IOException;
+
+    DumpWriter writeLong(long v) throws IOException;
+
+    DumpWriter writeFloat(float v) throws IOException;
+
+    DumpWriter writeDouble(double v) throws IOException;
+}
--- a/mx.graal/suite.py	Thu Oct 29 13:53:23 2015 +0100
+++ b/mx.graal/suite.py	Thu Oct 29 17:49:30 2015 +0100
@@ -926,7 +926,24 @@
       "javaCompliance" : _8_9,
       "annotationProcessors" : [ap("SERVICE")],
       "workingSets" : "Graal,Truffle,SPARC",
-    }
+    },
+
+    # ------------- Salver -------------
+
+    "com.oracle.graal.salver" : {
+      "subDir" : "graal",
+      "sourceDirs" : ["src"],
+      "dependencies" : [
+        "com.oracle.graal.java",
+      ],
+      "annotationProcessors" : [
+        ap("OPTIONS"),
+        ap("SERVICE"),
+      ],
+      "checkstyle" : "com.oracle.graal.graph",
+      "javaCompliance" : _8_9,
+      "workingSets" : "Graal",
+    },
   },
 
   "distributions" : {
@@ -974,6 +991,7 @@
         "com.oracle.graal.replacements.amd64",
         "com.oracle.graal.compiler.sparc",
         "com.oracle.graal.replacements.sparc",
+        "com.oracle.graal.salver",
       ],
       "distDependencies" : [
         "GRAAL_API",