package org.araqne.logdb.metadata; import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Invalidate; import org.apache.felix.ipojo.annotations.Requires; import org.apache.felix.ipojo.annotations.Validate; import org.araqne.logdb.FieldOrdering; import org.araqne.logdb.FunctionRegistry; import org.araqne.logdb.MetadataCallback; import org.araqne.logdb.MetadataProvider; import org.araqne.logdb.MetadataService; import org.araqne.logdb.QueryContext; import org.araqne.logdb.QueryParseException; import org.araqne.logdb.Row; @Component(name = "logdb-topthread-metadata") public class TopThreadMetadataProvider implements MetadataProvider, FieldOrdering { @Requires private MetadataService metadataService; @Requires private FunctionRegistry functionRegistry; @Validate public void start() { metadataService.addProvider(this); } @Invalidate public void stop() { if (metadataService != null) metadataService.removeProvider(this); } @Override public List<String> getFieldOrder() { return Arrays.asList("tid", "name", "state", "priority", "usage", "stacktrace"); } @Override public String getType() { return "topthreads"; } @Override public void verify(QueryContext context, String queryString) { if (!context.getSession().isAdmin()) throw new QueryParseException("95050", -1, -1, null); ThreadMXBean bean = ManagementFactory.getThreadMXBean(); if (!bean.isThreadCpuTimeSupported()) throw new QueryParseException("95051", -1, -1, null); if (!bean.isThreadCpuTimeEnabled()) throw new QueryParseException("95051", -1, -1, null); } @Override public void query(QueryContext context, String queryString, MetadataCallback callback) { ThreadMXBean bean = ManagementFactory.getThreadMXBean(); ArrayList<ThreadCpuUsage> usages = new ArrayList<ThreadCpuUsage>(); for (long tid : bean.getAllThreadIds()) { long time = bean.getThreadCpuTime(tid); usages.add(new ThreadCpuUsage(tid, time)); } try { Thread.sleep(1000); } catch (InterruptedException e) { } Map<Thread, StackTraceElement[]> stacks = Thread.getAllStackTraces(); for (long tid : bean.getAllThreadIds()) { ThreadCpuUsage usage = find(usages, tid); if (usage != null) usage.secondTime = bean.getThreadCpuTime(tid); } Collections.sort(usages); for (ThreadCpuUsage usage : usages) { long elapsed = usage.secondTime - usage.firstTime; // remove just created thread or sleeping threads (noisy) if (elapsed <= 0) continue; StringBuilder sb = new StringBuilder(); Thread t = findThread(stacks, usage.tid); if (t == null) continue; StackTraceElement[] stack = findStack(stacks, usage.tid); for (StackTraceElement el : stack) { sb.append(String.format("%s.%s %s\n", el.getClassName(), el.getMethodName(), getFileAndLineNumber(el))); } Map<String, Object> m = new HashMap<String, Object>(); m.put("tid", t.getId()); m.put("name", t.getName()); m.put("state", t.getState().toString()); m.put("priority", t.getPriority()); m.put("usage", elapsed); m.put("stacktrace", sb.toString()); callback.onPush(new Row(m)); } } private ThreadCpuUsage find(List<ThreadCpuUsage> usages, long tid) { for (ThreadCpuUsage usage : usages) if (usage.tid == tid) return usage; return null; } private Thread findThread(Map<Thread, StackTraceElement[]> stacks, long tid) { for (Thread t : stacks.keySet()) if (t.getId() == tid) return t; return null; } private StackTraceElement[] findStack(Map<Thread, StackTraceElement[]> stacks, long tid) { for (Thread t : stacks.keySet()) if (t.getId() == tid) return stacks.get(t); return null; } private String getFileAndLineNumber(StackTraceElement el) { if (el.getFileName() != null && el.getLineNumber() > 0) return String.format("(%s:%d)", el.getFileName(), el.getLineNumber()); else if (el.getFileName() != null && el.getLineNumber() <= 0) return String.format("(%s)", el.getFileName()); else return ""; } private static class ThreadCpuUsage implements Comparable<ThreadCpuUsage> { private long tid; private long firstTime; private long secondTime; public ThreadCpuUsage(long tid, long firstTime) { this.tid = tid; this.firstTime = firstTime; } @Override public int compareTo(ThreadCpuUsage o) { // descending order long self = secondTime - firstTime; long other = o.secondTime - o.firstTime; return (int) (other - self); } } }