/*
* Copyright 2010 NCHOVY
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.script;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.ThreadMXBean;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.management.MBeanServer;
import org.krakenapps.api.PathAutoCompleter;
import org.krakenapps.api.Script;
import org.krakenapps.api.ScriptArgument;
import org.krakenapps.api.ScriptContext;
import org.krakenapps.api.ScriptUsage;
import com.sun.management.HotSpotDiagnosticMXBean;
import com.sun.management.UnixOperatingSystemMXBean;
@SuppressWarnings("restriction")
public class SunPerfScript implements Script {
private ScriptContext context;
@Override
public void setScriptContext(ScriptContext arg0) {
this.context = arg0;
}
@ScriptUsage(description = "dump .hprof file", arguments = {
@ScriptArgument(name = "path", type = "string", description = "dump file path", autocompletion = PathAutoCompleter.class),
@ScriptArgument(name = "live only", type = "string", description = "if true dump only live objects") })
public void dumpHeap(String[] args) throws IOException {
MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean bean = ManagementFactory.newPlatformMXBeanProxy(platformMBeanServer,
"com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
bean.dumpHeap(args[0], Boolean.parseBoolean(args[1]));
}
public void system(String[] args) throws IOException {
OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
if (os instanceof com.sun.management.OperatingSystemMXBean) {
com.sun.management.OperatingSystemMXBean sunbean = (com.sun.management.OperatingSystemMXBean) os;
NumberFormat nf = NumberFormat.getNumberInstance();
context.printf("Process cpu time: %s\n", nf.format(sunbean.getProcessCpuTime()));
context.printf("Free phys memory: %s/%s\n", nf.format(sunbean.getFreePhysicalMemorySize()),
nf.format(sunbean.getTotalPhysicalMemorySize()));
context.printf("Free swap space: %s/%s\n", nf.format(sunbean.getFreeSwapSpaceSize()),
nf.format(sunbean.getTotalSwapSpaceSize()));
context.printf("Commited virtual memory: %s\n", nf.format(sunbean.getCommittedVirtualMemorySize()));
}
if (os instanceof UnixOperatingSystemMXBean) {
UnixOperatingSystemMXBean unixbean = (UnixOperatingSystemMXBean) os;
context.printf("open fd: %s/%s\n", unixbean.getOpenFileDescriptorCount(), unixbean.getMaxFileDescriptorCount());
}
}
public void topThreads(String[] args) throws InterruptedException {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
Map<Thread, StackTraceElement[]> stacks = Thread.getAllStackTraces();
if (!bean.isThreadCpuTimeSupported()) {
context.println("thread cpu time is not supported.");
return;
}
if (!bean.isThreadCpuTimeEnabled()) {
context.println("thread cpu time is not enabled.");
return;
}
ArrayList<ThreadCpuUsage> usages = new ArrayList<ThreadCpuUsage>();
for (long tid : bean.getAllThreadIds()) {
long time = bean.getThreadCpuTime(tid);
usages.add(new ThreadCpuUsage(tid, time));
}
Thread.sleep(200);
for (long tid : bean.getAllThreadIds()) {
ThreadCpuUsage usage = find(usages, tid);
if (usage != null)
usage.secondTime = bean.getThreadCpuTime(tid);
}
Collections.sort(usages);
context.println("Thread CPU Usages");
context.println("--------------------");
for (ThreadCpuUsage usage : usages) {
long elapsed = usage.secondTime - usage.firstTime;
// remove just created thread or sleeping threads (noisy)
if (elapsed <= 0)
continue;
context.printf("TID %d: %d\n", usage.tid, elapsed);
StackTraceElement[] stack = findStack(stacks, usage.tid);
for (StackTraceElement el : stack) {
context.printf("\t%s.%s %s\n", el.getClassName(), el.getMethodName(), getFileAndLineNumber(el));
}
}
}
private ThreadCpuUsage find(List<ThreadCpuUsage> usages, long tid) {
for (ThreadCpuUsage usage : usages)
if (usage.tid == tid)
return usage;
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);
}
}
}