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

io.github.linyimin0812.profiler.extension.enhance.sample.jvmprofiler.FlameGraph Maven / Gradle / Ivy

There is a newer version: 3.1.4
Show newest version
package io.github.linyimin0812.profiler.extension.enhance.sample.jvmprofiler;

import io.github.linyimin0812.profiler.common.utils.NameUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author linyimin
 **/
public class FlameGraph {
    public static final byte FRAME_JIT_COMPILED = 0;
    public static final byte FRAME_NATIVE = 1;

    private static final String FLAME_TEMPLATE_FILE_NAME = "flame-graph.html";

    private final Frame root = new Frame(FRAME_NATIVE);
    private int depth;


    public void parse(String flameTemplatePath, String outputPath, Map traceMap) throws IOException {

        for (Map.Entry entry : traceMap.entrySet()) {
            String[] trace = entry.getKey().split(";");
            addSample(trace, entry.getValue());
        }
        dump(flameTemplatePath, outputPath);
    }
    private void addSample(String[] trace, long ticks) {

        Frame frame = root;
        for (String s : trace) {
            frame = frame.addChild(s, ticks);
        }
        frame.addLeaf(ticks);

        depth = Math.max(depth, trace.length);
    }

    private void dump(String flameTemplatePath, String outputPath) throws IOException {

        Path destPath = Paths.get(outputPath);
        Files.deleteIfExists(destPath);
        Files.createDirectories(destPath.getParent());
        Files.createFile(destPath);

        try (BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(destPath), 32768);
             PrintStream out = new PrintStream(bos, false, "UTF-8")) {
            dump(flameTemplatePath, out);
        }
    }

    private void dump(String flameTemplatePath, PrintStream out) {
        int depth = this.depth + 1;

        String tail = getFlameGraphResource(flameTemplatePath);

        tail = printTill(out, tail, "/*height:*/300");
        out.print(Math.min(depth * 16, 32767));

        tail = printTill(out, tail, "/*title:*/");
        out.print("Wall clock profile");

        tail = printTill(out, tail, "/*reverse:*/false");
        out.print(false);

        tail = printTill(out, tail, "/*depth:*/0");
        out.print(depth);

        tail = printTill(out, tail, "/*frames:*/");

        printFrame(out, "all", root, 0, 0);

        tail = printTill(out, tail, "/*highlight:*/");
        out.print("");

        out.print(tail);
    }

    private String printTill(PrintStream out, String data, String till) {
        int index = data.indexOf(till);
        out.print(data.substring(0, index));
        return data.substring(index + till.length());
    }

    private void printFrame(PrintStream out, String title, Frame frame, int level, long x) {
        int type = frame.getType();

        if ((frame.inlined | frame.c1 | frame.interpreted) != 0 && frame.inlined < frame.total && frame.interpreted < frame.total) {
            out.println("f(" + level + "," + x + "," + frame.total + "," + type + ",'" + escape(title) + "'," +
                    frame.inlined + "," + frame.c1 + "," + frame.interpreted + ")");
        } else {
            out.println("f(" + level + "," + x + "," + frame.total + "," + type + ",'" + escape(title) + "')");
        }

        x += frame.self;
        for (Map.Entry e : frame.entrySet()) {
            Frame child = e.getValue();
            if (child.total >= 0) {
                printFrame(out, e.getKey(), child, level + 1, x);
            }
            x += child.total;
        }
    }

    private static String escape(String s) {
        if (s.indexOf('\\') >= 0) s = s.replace("\\", "\\\\");
        if (s.indexOf('\'') >= 0) s = s.replace("'", "\\'");
        return s;
    }

    private static String getFlameGraphResource(String flameTemplatePath) {

        try (InputStream stream = Files.newInputStream(Paths.get(flameTemplatePath))) {

            ByteArrayOutputStream result = new ByteArrayOutputStream();
            byte[] buffer = new byte[64 * 1024];
            for (int length; (length = stream.read(buffer)) != -1; ) {
                result.write(buffer, 0, length);
            }
            return result.toString("UTF-8");
        } catch (IOException e) {
            throw new IllegalStateException("Can't load resource with name " + Paths.get(NameUtil.getTemplatePath() , FLAME_TEMPLATE_FILE_NAME));
        }
    }

    static class Frame extends TreeMap {
        final byte type;
        long total;
        long self;
        long inlined, c1, interpreted;

        Frame(byte type) {
            this.type = type;
        }

        byte getType() {
            return type;
        }

        private Frame getChild(String title, byte type) {
            Frame child = super.get(title);
            if (child == null) {
                super.put(title, child = new Frame(type));
            }
            return child;
        }

        Frame addChild(String title, long ticks) {
            total += ticks;

            Frame child;

            if (StringUtils.contains(title, ".Unsafe.")) {
                child = getChild(title, FRAME_NATIVE);
            } else {
                child = getChild(title, FRAME_JIT_COMPILED);
            }

            return child;
        }

        void addLeaf(long ticks) {
            total += ticks;
            self += ticks;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy