All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jruby.truffle.tools.InstrumentationServerManager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. This
 * code is released under a tri EPL/GPL/LGPL license. You can use it,
 * redistribute it and/or modify it under the terms of the:
 *
 * Eclipse Public License version 1.0
 * GNU General Public License version 2
 * GNU Lesser General Public License version 2.1
 */
package org.jruby.truffle.tools;

import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.string.StringUtils;
import org.jruby.truffle.language.SafepointAction;
import org.jruby.truffle.language.backtrace.Backtrace;
import org.jruby.truffle.language.backtrace.BacktraceFormatter;
import org.jruby.truffle.language.control.JavaException;
import org.jruby.truffle.tools.simpleshell.SimpleShell;

import java.io.IOException;
import java.io.OutputStream;
import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.List;

@SuppressWarnings("restriction")
public class InstrumentationServerManager {

    private final RubyContext context;
    private final HttpServer server;

    private volatile boolean shuttingDown = false;

    public InstrumentationServerManager(RubyContext context, int port) {
        this.context = context;

        final InetAddress host = InetAddress.getLoopbackAddress();
        HttpServer server = null;
        try {
            server = HttpServer.create(new InetSocketAddress(host, port), 0);
        } catch (BindException e) {
            System.err.println("Port " + port + " was already in use: " + e);
            throw new JavaException(e);
        } catch (IOException e) {
            throw new JavaException(e);
        }

        this.server = server;
    }

    public void start() {
        server.createContext("/stacks", new HttpHandler() {

            @Override
            public void handle(HttpExchange httpExchange) {
                try {
                    final StringBuilder builder = new StringBuilder();

                    context.getSafepointManager().pauseAllThreadsAndExecuteFromNonRubyThread(false, new SafepointAction() {
                        @Override
                        public void accept(DynamicObject thread, Node currentNode) {
                            synchronized (this) {
                                try {
                                    final Backtrace backtrace = context.getCallStack().getBacktrace(null);

                                    final List lines = BacktraceFormatter.createDefaultFormatter(context)
                                            .formatBacktrace(context, null, backtrace);

                                    builder.append(StringUtils.format("#%d %s",
                                            Thread.currentThread().getId(),
                                            Thread.currentThread().getName()));

                                    builder.append("\n");

                                    for (String line : lines) {
                                        builder.append(line);
                                        builder.append("\n");
                                    }

                                    builder.append("\n");
                                } catch (Throwable e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    });

                    final byte[] bytes = builder.toString().getBytes("UTF-8");

                    httpExchange.getResponseHeaders().set("Content-Type", "text/plain");
                    httpExchange.sendResponseHeaders(200, bytes.length);

                    final OutputStream stream = httpExchange.getResponseBody();
                    stream.write(bytes);
                    stream.close();
                } catch (IOException e) {
                    if (shuttingDown) {
                        return;
                    }

                    e.printStackTrace();
                }
            }

        });

        server.createContext("/break", httpExchange -> {
            try {
                final Thread mainThread = Layouts.FIBER.getThread(
                        Layouts.THREAD.getFiberManager(context.getThreadManager().getRootThread())
                                .getCurrentFiber());

                context.getSafepointManager().pauseThreadAndExecuteLaterFromNonRubyThread(mainThread, (thread, currentNode) -> new SimpleShell(context).run(Truffle.getRuntime().getCurrentFrame()
                        .getFrame(FrameInstance.FrameAccess.MATERIALIZE, true).materialize(), currentNode));

                httpExchange.getResponseHeaders().set("Content-Type", "text/plain");
                httpExchange.sendResponseHeaders(200, 0);
                httpExchange.getResponseBody().close();
            } catch (IOException e) {
                if (shuttingDown) {
                    return;
                }

                e.printStackTrace();
            }
        });

        server.start();
    }

    public void shutdown() {
        if (server != null) {
            shuttingDown = true;
            // Leave it some time to send the remaining bytes.
            server.stop(1);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy