Profiler.java 11.7 KB
/*
 * Decompiled with CFR 0_118.
 */
package com.day.crx.sling.server.impl.jmx;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.StringReader;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Profiler
implements Runnable {
    private static Instrumentation instrumentation;
    private static final String LINE_SEPARATOR;
    private static final int MAX_ELEMENTS = 1000;
    public int interval = 2;
    public int depth = 48;
    public boolean paused;
    public boolean sumClasses;
    private int pid;
    private final String[] ignoreLines = new String[0];
    private final String[] ignorePackages = "java,sun,com.sun.".split(",");
    private final String[] ignoreThreads = "java.lang.Object.wait,java.lang.Thread.dumpThreads,java.lang.Thread.getThreads,java.lang.Thread.sleep,java.lang.UNIXProcess.waitForProcessExit,java.net.PlainSocketImpl.accept,java.net.PlainSocketImpl.socketAccept,java.net.SocketInputStream.socketRead,java.net.SocketOutputStream.socketWrite,sun.awt.windows.WToolkit.eventLoop,sun.misc.Unsafe.park,dalvik.system.VMStack.getThreadStackTrace,dalvik.system.NativeStart.run".split(",");
    private volatile boolean stop;
    private final HashMap<String, Integer> counts = new HashMap();
    private final HashMap<String, Integer> summary = new HashMap();
    private int minCount = 1;
    private int total;
    private Thread thread;
    private long start;
    private long time;

    public static void premain(String agentArgs, Instrumentation inst) {
        instrumentation = inst;
    }

    public static Instrumentation getInstrumentation() {
        return instrumentation;
    }

    public static /* varargs */ void main(String ... args) {
        new Profiler().run(args);
    }

    private /* varargs */ void run(String ... args) {
        if (args.length == 0) {
            System.out.println("Show profiling data");
            System.out.println("Usage: " + this.getClass().getName() + " <pid>");
            System.out.println("Processes:");
            String processes = Profiler.exec("jps", "-l");
            System.out.println(processes);
            return;
        }
        this.pid = Integer.parseInt(args[0]);
        this.start = System.currentTimeMillis();
        long last = 0;
        do {
            this.tick();
            long t = System.currentTimeMillis();
            if (t - last <= 5000) continue;
            this.time = System.currentTimeMillis() - this.start;
            System.out.println(this.getTopTraces(3));
            last = t;
        } while (true);
    }

    private static List<Object[]> getRunnableStackTraces() {
        ArrayList<Object[]> list = new ArrayList<Object[]>();
        Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
        for (Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
            StackTraceElement[] dump;
            Thread t = entry.getKey();
            if (t.getState() != Thread.State.RUNNABLE || (dump = entry.getValue()) == null || dump.length == 0) continue;
            list.add(dump);
        }
        return list;
    }

    private static List<Object[]> readRunnableStackTraces(int pid) {
        ArrayList<Object[]> list = new ArrayList<Object[]>();
        try {
            String line;
            String jstack = Profiler.exec("jstack", "" + pid);
            LineNumberReader r = new LineNumberReader(new StringReader(jstack));
            while ((line = r.readLine()) != null) {
                if (!line.startsWith("\"")) continue;
                line = r.readLine();
                if (line == null) break;
                if (!(line = line.trim()).startsWith("java.lang.Thread.State: RUNNABLE")) continue;
                ArrayList<String> stack = new ArrayList<String>();
                while ((line = r.readLine()) != null) {
                    if ((line = line.trim()).startsWith("- ")) continue;
                    if (!line.startsWith("at ")) break;
                    line = line.substring(3).trim();
                    stack.add(line);
                }
                if (stack.size() <= 0) continue;
                String[] s = stack.toArray(new String[stack.size()]);
                list.add(s);
            }
            return list;
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static /* varargs */ String exec(String ... args) {
        ByteArrayOutputStream err = new ByteArrayOutputStream();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            Process p = Runtime.getRuntime().exec(args);
            Profiler.copyInThread(p.getInputStream(), out);
            Profiler.copyInThread(p.getErrorStream(), err);
            p.waitFor();
            String e = new String(err.toByteArray(), "UTF-8");
            if (e.length() > 0) {
                throw new RuntimeException(e);
            }
            String output = new String(out.toByteArray(), "UTF-8");
            return output;
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void copyInThread(final InputStream in, final OutputStream out) {
        new Thread("Profiler stream copy"){

            @Override
            public void run() {
                byte[] buffer = new byte[4096];
                try {
                    int len;
                    while ((len = in.read(buffer, 0, buffer.length)) >= 0) {
                        out.write(buffer, 0, len);
                    }
                }
                catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }.run();
    }

    public Profiler startCollecting() {
        this.thread = new Thread((Runnable)this, "Profiler");
        this.thread.setDaemon(true);
        this.thread.start();
        return this;
    }

    public Profiler stopCollecting() {
        this.stop = true;
        if (this.thread != null) {
            try {
                this.thread.join();
            }
            catch (InterruptedException e) {
                // empty catch block
            }
            this.thread = null;
        }
        return this;
    }

    @Override
    public void run() {
        this.start = System.currentTimeMillis();
        while (!this.stop) {
            try {
                this.tick();
                continue;
            }
            catch (Throwable t) {
                // empty catch block
                break;
            }
        }
        this.time = System.currentTimeMillis() - this.start;
    }

    private void tick() {
        if (this.interval > 0) {
            if (this.paused) {
                return;
            }
            try {
                Thread.sleep(this.interval);
            }
            catch (Exception e) {
                // empty catch block
            }
        }
        List<Object[]> list = this.pid != 0 ? Profiler.readRunnableStackTraces(this.pid) : Profiler.getRunnableStackTraces();
        for (Object[] dump : list) {
            if (Profiler.startsWithAny(dump[0].toString(), this.ignoreThreads)) continue;
            StringBuilder buff = new StringBuilder();
            String last = null;
            boolean packageCounts = false;
            int j = 0;
            for (int i = 0; i < dump.length && j < this.depth; ++i) {
                String el = dump[i].toString();
                if (el.equals(last) || Profiler.startsWithAny(el, this.ignoreLines)) continue;
                last = el;
                buff.append("at ").append(el).append(LINE_SEPARATOR);
                if (!packageCounts && !Profiler.startsWithAny(el, this.ignorePackages)) {
                    char c;
                    int index;
                    packageCounts = true;
                    for (index = 0; index < el.length() && (c = el.charAt(index)) != '(' && !Character.isUpperCase(c); ++index) {
                    }
                    if (index > 0 && el.charAt(index - 1) == '.') {
                        --index;
                    }
                    if (this.sumClasses) {
                        int m = el.indexOf(46, index + 1);
                        index = m >= 0 ? m : index;
                    }
                    String groupName = el.substring(0, index);
                    Profiler.increment(this.summary, groupName, 0);
                }
                ++j;
            }
            if (buff.length() <= 0) continue;
            this.minCount = Profiler.increment(this.counts, buff.toString().trim(), this.minCount);
            ++this.total;
        }
    }

    private static boolean startsWithAny(String s, String[] prefixes) {
        for (String p : prefixes) {
            if (p.length() <= 0 || !s.startsWith(p)) continue;
            return true;
        }
        return false;
    }

    private static int increment(HashMap<String, Integer> map, String trace, int minCount) {
        Integer oldCount = map.get(trace);
        if (oldCount == null) {
            map.put(trace, 1);
        } else {
            map.put(trace, oldCount + 1);
        }
        while (map.size() > 1000) {
            Iterator<Map.Entry<String, Integer>> ei = map.entrySet().iterator();
            while (ei.hasNext()) {
                Map.Entry<String, Integer> e = ei.next();
                if (e.getValue() > minCount) continue;
                ei.remove();
            }
            if (map.size() <= 1000) continue;
            ++minCount;
        }
        return minCount;
    }

    public String getTop(int count) {
        this.stopCollecting();
        return this.getTopTraces(count);
    }

    private String getTopTraces(int count) {
        StringBuilder buff = new StringBuilder();
        buff.append("Profiler: top ").append(count).append(" stack trace(s) of ").append(this.time).append(" ms:").append(LINE_SEPARATOR);
        if (this.counts.size() == 0) {
            buff.append("(none)").append(LINE_SEPARATOR);
        }
        HashMap<String, Integer> copy = new HashMap<String, Integer>(this.counts);
        Profiler.appendTop(buff, copy, count, this.total, false);
        buff.append("summary:").append(LINE_SEPARATOR);
        copy = new HashMap<String, Integer>(this.summary);
        Profiler.appendTop(buff, copy, count, this.total, true);
        buff.append('.');
        return buff.toString();
    }

    private static void appendTop(StringBuilder buff, HashMap<String, Integer> map, int count, int total, boolean table) {
        int x = 0;
        int min = 0;
        do {
            int highest = 0;
            Map.Entry<String, Integer> best = null;
            for (Map.Entry<String, Integer> el : map.entrySet()) {
                if (el.getValue() <= highest) continue;
                best = el;
                highest = el.getValue();
            }
            if (best == null) break;
            map.remove(best.getKey());
            if (++x >= count) {
                if (best.getValue() < min) break;
                min = best.getValue();
            }
            int c = best.getValue();
            int percent = 100 * c / Math.max(total, 1);
            if (table) {
                if (percent <= 1) continue;
                buff.append(percent).append("%: ").append(best.getKey()).append(LINE_SEPARATOR);
                continue;
            }
            buff.append(c).append('/').append(total).append(" (").append(percent).append("%):").append(LINE_SEPARATOR).append(best.getKey()).append(LINE_SEPARATOR);
        } while (true);
    }

    static {
        LINE_SEPARATOR = System.getProperty("line.separator", "\n");
    }

}