/**
* jvmtop - java monitoring for the command-line
*
* Copyright (C) 2013 by Patric Rufflar. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.jvmtop.profiler;
import java.lang.Thread.State;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import com.jvmtop.monitor.VMInfo;
/**
* Experimental and very basic sampling-based CPU-Profiler.
*
* It uses package excludes to filter common 3rd party libraries which often
* distort application problems.
*
* @author paru
*
*/
public class CPUSampler
{
private ThreadMXBean threadMxBean_ = null;
private ConcurrentMap<String, MethodStats> data_ = new ConcurrentHashMap<String, MethodStats>();
private long beginCPUTime_ = 0;
private AtomicLong totalThreadCPUTime_ = new AtomicLong(
0);
//TODO: these exception list should be expanded to the most common 3rd-party library packages
private List<String> filter = Arrays
.asList(new String[] {
"org.eclipse.", "org.apache.", "java.", "sun.", "com.sun.", "javax.",
"oracle.", "com.trilead.", "org.junit.", "org.mockito.",
"org.hibernate.", "com.ibm.", "com.caucho."
});
private ConcurrentMap<Long, Long> threadCPUTime = new ConcurrentHashMap<Long, Long>();
private AtomicLong updateCount_ = new AtomicLong(
0);
private VMInfo vmInfo_;
/**
* @param threadMxBean
* @throws Exception
*/
public CPUSampler(VMInfo vmInfo) throws Exception
{
super();
threadMxBean_ = vmInfo.getThreadMXBean();
beginCPUTime_ = vmInfo.getProxyClient().getProcessCpuTime();
vmInfo_ = vmInfo;
}
public List<MethodStats> getTop(int limit)
{
ArrayList<MethodStats> statList = new ArrayList<MethodStats>(data_.values());
Collections.sort(statList);
return statList.subList(0, Math.min(limit, statList.size()));
}
public long getTotal()
{
return totalThreadCPUTime_.get();
}
public void update() throws Exception
{
boolean samplesAcquired = false;
for (ThreadInfo ti : threadMxBean_.dumpAllThreads(false, false))
{
long cpuTime = threadMxBean_.getThreadCpuTime(ti.getThreadId());
Long tCPUTime = threadCPUTime.get(ti.getThreadId());
if (tCPUTime == null)
{
tCPUTime = 0L;
}
else
{
Long deltaCpuTime = (cpuTime - tCPUTime);
if (ti.getStackTrace().length > 0
&& ti.getThreadState() == State.RUNNABLE
) {
for (StackTraceElement stElement : ti.getStackTrace()) {
if (isReallySleeping(stElement)) {
break;
}
if (isFiltered(stElement)) {
continue;
}
String key = stElement.getClassName() + "."
+ stElement.getMethodName();
data_.putIfAbsent(key, new MethodStats(stElement.getClassName(),
stElement.getMethodName()));
data_.get(key).getHits().addAndGet(deltaCpuTime);
totalThreadCPUTime_.addAndGet(deltaCpuTime);
samplesAcquired = true;
break;
}
}
}
threadCPUTime.put(ti.getThreadId(), cpuTime);
}
if (samplesAcquired)
{
updateCount_.incrementAndGet();
}
}
public Long getUpdateCount()
{
return updateCount_.get();
}
private boolean isReallySleeping(StackTraceElement se) {
return se.getClassName().equals("sun.nio.ch.EPollArrayWrapper") &&
se.getMethodName().equals("epollWait");
}
public boolean isFiltered(StackTraceElement se) {
for (String filteredPackage : filter) {
if (se.getClassName().startsWith(filteredPackage)) {
return true;
}
}
return false;
}
}