package com.performizeit.mjprof.plugins.dataSource; import com.performizeit.jmxsupport.JMXConnection; import com.performizeit.mjprof.plugin.types.DataSource; import com.performizeit.mjprof.api.Plugin; import com.performizeit.mjprof.model.Profile; import com.performizeit.mjprof.api.Param; import com.performizeit.mjprof.parser.ThreadDump; import com.performizeit.mjprof.parser.ThreadInfo; import com.performizeit.plumbing.GeneratorHandler; import javax.management.openmbean.CompositeData; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import static com.performizeit.mjprof.parser.ThreadInfoProps.*; // host:port or pid , freq,period ,user,pass @SuppressWarnings("unused") @Plugin(name = "jmx", params = {@Param("host:port|MainClass|pid"), @Param(type = int.class, value = "count", optional = true, defaultValue = "1"), @Param(type = int.class, value = "sleep", optional = true, defaultValue = "5000"), @Param(value = "username", optional = true), @Param(value = "passwd", optional = true) }, description = "Generates thread dumps via JMX ") public class JmxDataSourcePlugin implements DataSource, GeneratorHandler<ThreadDump> { protected final int sleep; protected final int count; protected int iter = 0; protected String hostPortPid; protected JMXConnection server = null; protected long lastIterTime = 0; public JmxDataSourcePlugin(String hostPortPid, int count, int sleep, String user, String pass) { this.hostPortPid = hostPortPid; this.count = count; this.sleep = sleep; user = user.trim().isEmpty() ? null : user; pass = pass.trim().isEmpty() ? null : pass; try { try { Integer.parseInt(hostPortPid); server = new JMXConnection(hostPortPid); } catch (NumberFormatException e) { if (!hostPortPid.contains(":")) { int pid = JPSUtil.lookupProcessId(hostPortPid); if (pid == -1) { System.err.println("Process id for main class '" + hostPortPid + "' could not be resolved"); System.exit(1); } else { server = new JMXConnection(Integer.toString(pid)); } } else { server = new JMXConnection(hostPortPid, user, pass); } } } catch (Exception e) { System.err.println("ERROR: Unable to open JMX connection for" + hostPortPid); System.exit(1); server = null; } } protected ThreadDump getThreadDump() throws Exception { ThreadDump threadDump = new ThreadDump(); long iterStart = System.currentTimeMillis(); try { threadDump.setHeader((new Date()).toString() + "\nThread dump via JMX of process " + hostPortPid); long[] threadsIds = server.getThreadIds(); CompositeData[] threads = server.getThreads(threadsIds, 10000); for (int thrdidx = 0; thrdidx < threadsIds.length; thrdidx++) { CompositeData thread = threads[thrdidx]; HashMap<String, Object> props = getProps(thread); ThreadInfo threadInfo = new ThreadInfo(props); threadDump.addThreadInfo(threadInfo); } } finally { lastIterTime = System.currentTimeMillis() - iterStart; iter++; } return threadDump; } protected HashMap<String, Object> getProps(CompositeData thread) { HashMap<String, Object> props = new HashMap<String, Object>(); props.put(NAME, thread.get("threadName")); props.put(TID, thread.get("threadId")); props.put(STATE, thread.get("threadState")); CompositeData[] stec = (CompositeData[]) thread.get("stackTrace"); StackTraceElement[] ste = new StackTraceElement[stec.length]; for (int i = 0; i < stec.length; i++) { ste[i] = new StackTraceElement((String) stec[i].get("className"), (String) stec[i].get("methodName"), (String) stec[i].get("fileName"), (Integer) stec[i].get("lineNumber")); } Profile p = new Profile(); p.addSingle(ste); props.put(STACK, p); return props; } @Override public ThreadDump generate() { try { return getThreadDump(); } catch (Exception e) { throw new RuntimeException(e); } } @Override public boolean isDone() { return iter >= count; } @Override public void sleepBetweenIteration() { if (lastIterTime < sleep) try { Thread.sleep(sleep - lastIterTime); } catch (InterruptedException e) { e.printStackTrace(); } } }