package com.performizeit.mjprof.plugins.dataSource;
import com.performizeit.mjprof.plugin.types.DataSource;
import com.performizeit.mjprof.api.Plugin;
import com.performizeit.mjprof.api.Param;
import com.performizeit.mjprof.parser.ThreadDump;
import com.performizeit.plumbing.GeneratorHandler;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.AttachNotSupportedException;
import sun.jvmstat.monitor.*;
import sun.tools.attach.HotSpotVirtualMachine;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.ArrayList;
@SuppressWarnings("unused")
@Plugin(name = "jstack", params = {@Param(type = String.class, value = "pid|mainClassName"),
@Param(type = int.class, value = "count", optional = true, defaultValue = "1"),
@Param(type = int.class, value = "sleep", optional = true, defaultValue = "5000")},
description = "Generates dumps using jstack")
public class JstackDataSourcePlugin implements DataSource, GeneratorHandler<ThreadDump> {
private final int count;
private int iter = 0;
private final int sleep;
private long lastIterTime = 0;
int pid;
public JstackDataSourcePlugin(String pidStr, int count, int sleep) {
try {
this.pid = Integer.parseInt(pidStr);
} catch (NumberFormatException e) {
try {
pid = JPSUtil.lookupProcessId(pidStr);
if (pid == -1) {
System.err.println("Process id for main class '" + pidStr + "' could not be resolved");
System.exit(1);
}
} catch (Exception ne) {
System.err.println("Process id for main class '" + pidStr + "' could not be resolved");
System.exit(1);
}
}
this.count = count;
this.sleep = sleep;
}
public ArrayList<ThreadDump> getThreadDumps() {
ArrayList<ThreadDump> dumps = new ArrayList<>();
try {
for (iter = 0; iter < count; iter++) {
long iterStart = System.currentTimeMillis();
dumps.add(getThreadDump());
long iterEnd = System.currentTimeMillis();
if (iter < count - 1 && iterEnd - iterStart < sleep)
Thread.sleep(sleep - (iterEnd - iterStart));
}
} catch (Exception e) {
e.printStackTrace();
}
return dumps;
}
public String runjStackCommandLine() throws IOException, InterruptedException {
String[] commands = {System.getProperty("java.home") + "/../bin/jstack", Integer.toString(pid)};
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(commands);
InputStream stdin = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stdin);
BufferedReader br = new BufferedReader(isr);
StreamDataSourcePluginBase sds = new StreamDataSourcePluginBase() {
};
sds.setReader(br);
int ret = proc.waitFor();
if (ret != 0) {
System.err.println("Executing jstack for process " + pid + " failed");
}
return sds.getStackStringFromReader();
}
private ThreadDump getThreadDump() {
long iterStart = System.currentTimeMillis();
try {
// long start = System.currentTimeMillis();
// String str = runjStackCommandLine();
String[] params = {"-;l"};
String str = runThreadDump((new Integer(pid)).toString(), params);
// System.err.println("tm ="+ (System.currentTimeMillis()-start));
ThreadDump r;
if (str == null) {
r = null;
} else r = new ThreadDump(str);
iter++;
return r;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lastIterTime = System.currentTimeMillis() - iterStart;
}
}
@Override
public ThreadDump generate() {
return getThreadDump();
}
@Override
public boolean isDone() {
return iter >= count;
}
@Override
public void sleepBetweenIteration() {
if (lastIterTime < sleep)
try {
Thread.sleep(sleep - lastIterTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static String runThreadDump(String pid, String args[]) throws Exception {
StringBuilder sb = new StringBuilder();
VirtualMachine vm = null;
try {
vm = VirtualMachine.attach(pid);
} catch (Exception x) {
String msg = x.getMessage();
if (msg != null) {
System.err.println(pid + ": " + msg);
} else {
x.printStackTrace();
}
if ((x instanceof AttachNotSupportedException) &&
(loadSAClass() != null)) {
System.err.println("The -F option can be used when the target " +
"process is not responding");
}
System.exit(1);
}
// Cast to HotSpotVirtualMachine as this is implementation specific
// method.
InputStream in = ((HotSpotVirtualMachine) vm).remoteDataDump((Object[]) args);
// read to EOF and just print output
byte b[] = new byte[256];
int n;
do {
n = in.read(b);
if (n > 0) {
String s = new String(b, 0, n, "UTF-8");
sb.append(s);
}
} while (n > 0);
in.close();
vm.detach();
return sb.toString();
}
private static Class loadSAClass() {
//
// Attempt to load JStack class - we specify the system class
// loader so as to cater for development environments where
// this class is on the boot class path but sa-jdi.jar is on
// the system class path. Once the JDK is deployed then both
// tools.jar and sa-jdi.jar are on the system class path.
//
try {
return Class.forName("sun.jvm.hotspot.tools.JStack", true,
ClassLoader.getSystemClassLoader());
} catch (Exception x) {
}
return null;
}
public static void main(String[] args) throws MonitorException, URISyntaxException {
System.out.println((JPSUtil.lookupProcessId("My4tApp")));
}
}