/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.visualvm.sampler.cpu;
import com.sun.tools.visualvm.core.datasupport.Utils;
import com.sun.tools.visualvm.core.options.GlobalPreferences;
import com.sun.tools.visualvm.core.ui.components.DataViewComponent;
import com.sun.tools.visualvm.sampler.AbstractSamplerSupport;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.management.ThreadInfo;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.SwingUtilities;
import org.netbeans.lib.profiler.common.ProfilingSettings;
import org.netbeans.lib.profiler.common.ProfilingSettingsPresets;
import org.netbeans.lib.profiler.common.filters.SimpleFilter;
import org.netbeans.lib.profiler.global.InstrumentationFilter;
import org.netbeans.lib.profiler.results.cpu.CPUResultsSnapshot;
import org.netbeans.lib.profiler.results.cpu.CPUResultsSnapshot.NoDataAvailableException;
import org.netbeans.lib.profiler.results.cpu.StackTraceSnapshotBuilder;
import org.netbeans.modules.profiler.LoadedSnapshot;
import org.netbeans.modules.profiler.ResultsManager;
import org.openide.util.NbBundle;
/**
*
* @author Jiri Sedlacek
* @author Tomas Hurka
*/
public abstract class CPUSamplerSupport extends AbstractSamplerSupport {
private final ThreadInfoProvider threadInfoProvider;
private final SnapshotDumper snapshotDumper;
private final ThreadDumper threadDumper;
private Timer timer;
private TimerTask samplerTask;
private final Refresher refresher;
private int refreshRate;
private StackTraceSnapshotBuilder builder;
private volatile boolean sampleRunning;
private final Object updateLock = new Object();
private long currentLiveUpdate;
private long lastLiveUpdate;
private CPUView cpuView;
private DataViewComponent.DetailsView[] detailsViews;
private javax.swing.Timer threadCPUTimer;
private Refresher threadCPURefresher;
private ThreadsCPUView threadCPUView;
private ThreadsCPU threadsCPU;
public CPUSamplerSupport(ThreadInfoProvider tip, ThreadsCPU tcpu, SnapshotDumper snapshotDumper, ThreadDumper threadDumper) {
threadInfoProvider = tip;
threadsCPU = tcpu;
this.snapshotDumper = snapshotDumper;
this.threadDumper = threadDumper;
refreshRate = GlobalPreferences.sharedInstance().getMonitoredDataPoll() * 1000;
refresher = new Refresher() {
public void setRefreshRate(int rr) {
CPUSamplerSupport.this.refreshRate = rr;
}
public int getRefreshRate() {
return CPUSamplerSupport.this.refreshRate;
}
protected boolean checkRefresh() {
return samplerTask != null && cpuView.isShowing();
}
protected void doRefresh() {
doRefreshImpl();
}
};
if (threadsCPU != null) {
threadCPURefresher = new Refresher() {
public final boolean checkRefresh() {
if (threadCPUTimer == null) return false;
if (!threadCPUTimer.isRunning()) return false;
return threadCPUView.isShowing();
}
public final void doRefresh() {
doRefreshImpl(threadCPUTimer, threadCPUView);
}
public final void setRefreshRate(int refreshRate) {
threadCPUTimer.setDelay(refreshRate);
threadCPUTimer.setInitialDelay(refreshRate);
threadCPUTimer.restart();
}
public final int getRefreshRate() {
return threadCPUTimer.getDelay();
}
};
}
}
public DataViewComponent.DetailsView[] getDetailsView() {
if (detailsViews == null) {
cpuView = new CPUView(refresher, snapshotDumper, threadDumper);
detailsViews = new DataViewComponent.DetailsView[threadsCPU != null ? 2:1];
detailsViews[0] = new DataViewComponent.DetailsView(NbBundle.getMessage(
CPUSamplerSupport.class, "LBL_Cpu_samples"), null, 10, cpuView, null); // NOI18N
if (threadsCPU != null) {
threadCPUView = new ThreadsCPUView(threadCPURefresher);
detailsViews[1] = new DataViewComponent.DetailsView(NbBundle.getMessage(
CPUSamplerSupport.class, "LBL_ThreadAlloc"), null, 20, threadCPUView, null); // NOI18N
}
}
cpuView.initSession();
if (threadsCPU != null) {
threadCPUView.initSession();
}
return detailsViews.clone();
}
public boolean startSampling(ProfilingSettings settings, int samplingRate, int refreshRate) {
InstrumentationFilter filter = new InstrumentationFilter();
SimpleFilter sf = (SimpleFilter)settings.getSelectedInstrumentationFilter();
filter.setFilterStrings(sf.getFilterValue());
filter.setFilterType(convertFilterType(sf.getFilterType()));
builder = snapshotDumper.getNewBuilder(filter);
refresher.setRefreshRate(refreshRate);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
cpuView.setResultsPanel(new SampledLivePanel(builder));
}
});
if (timer == null) timer = getTimer();
samplerTask = new SamplerTask(builder);
timer.scheduleAtFixedRate(samplerTask, 0, samplingRate);
if (threadsCPU != null) {
threadCPUTimer = new javax.swing.Timer(refreshRate, new ActionListener() {
public void actionPerformed(ActionEvent e) {
threadCPURefresher.refresh();
}
});
threadCPURefresher.setRefreshRate(refreshRate);
}
return true;
}
public synchronized void stopSampling() {
if (samplerTask != null) {
samplerTask.cancel();
samplerTask = null;
}
if (threadCPUTimer != null) {
threadCPUTimer.stop();
threadCPUTimer = null;
}
}
public synchronized void terminate() {
if (timer != null) {
timer.cancel();
timer = null;
}
if (cpuView != null) cpuView.terminate();
if (threadCPUView != null) threadCPUView.terminate();
builder = null; // release data
}
private void doRefreshImpl() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (samplerTask == null) return;
if (!sampleRunning) {
synchronized (updateLock) {
lastLiveUpdate = currentLiveUpdate;
cpuView.refresh();
}
} else {
SwingUtilities.invokeLater(this);
}
}
});
}
private void doRefreshImpl(final javax.swing.Timer stimer, final ThreadsCPUView view) {
if (!stimer.isRunning() || view.isPaused()) return;
try {
timer.schedule(new TimerTask() {
public void run() {
try {
if (!stimer.isRunning()) return;
doRefreshImplImpl(threadsCPU.getThreadsCPUInfo(), view);
} catch (Exception e) {
terminate();
}
}
}, 0);
} catch (Exception e) {
terminate();
}
}
private void doRefreshImplImpl(final ThreadsCPUInfo info, final ThreadsCPUView view) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
view.refresh(info);
}
});
}
private int convertFilterType(int simpleFilterrType) {
if (simpleFilterrType == SimpleFilter.SIMPLE_FILTER_NONE) {
return InstrumentationFilter.INSTR_FILTER_NONE;
}
if (simpleFilterrType == SimpleFilter.SIMPLE_FILTER_EXCLUSIVE) {
return InstrumentationFilter.INSTR_FILTER_EXCLUSIVE;
}
if (simpleFilterrType == SimpleFilter.SIMPLE_FILTER_INCLUSIVE) {
return InstrumentationFilter.INSTR_FILTER_INCLUSIVE;
}
throw new IllegalArgumentException("type "+simpleFilterrType); // NOI18N
}
private class SamplerTask extends TimerTask {
private final StackTraceSnapshotBuilder builder;
private final Set samplingThreads = new HashSet();
public SamplerTask(StackTraceSnapshotBuilder builder) {
this.builder = builder;
}
public void run() {
if (sampleRunning) return;
sampleRunning = true;
synchronized (updateLock) {
try {
ThreadInfo[] infos = threadInfoProvider.dumpAllThreads();
long timestamp = System.nanoTime();
String samplingThreadName = findSamplingThread(infos);
if (samplingThreadName != null) {
if (samplingThreads.add(samplingThreadName)) {
// System.out.println("New ignored thread: "+samplingThreadName);
builder.setIgnoredThreads(samplingThreads);
}
}
builder.addStacktrace(infos, timestamp);
currentLiveUpdate = timestamp / 1000000;
if (currentLiveUpdate - lastLiveUpdate >= refreshRate)
refresher.refresh();
} catch (Throwable ex) {
terminate();
} finally {
sampleRunning = false;
}
}
}
private String findSamplingThread(ThreadInfo[] infos) {
// for (ThreadInfo info : infos) {
// if (info.getThreadState() == Thread.State.RUNNABLE) {
// StackTraceElement[] stack = info.getStackTrace();
//
// if (stack.length > 0) {
// StackTraceElement topStack = stack[0];
//
// if (!topStack.isNativeMethod()) {
// continue;
// }
// if (!"sun.management.ThreadImpl".equals(topStack.getClassName())) { // NOI18N
// continue;
// }
// if ("getThreadInfo0".equals(topStack.getMethodName())) {
// return info.getThreadName();
// }
// }
// }
// }
return null;
}
}
public static abstract class ThreadDumper {
public abstract void takeThreadDump(boolean openView);
}
public static abstract class SnapshotDumper {
private StackTraceSnapshotBuilder builder;
StackTraceSnapshotBuilder getNewBuilder(InstrumentationFilter filter) {
builder = new StackTraceSnapshotBuilder(1,filter);
return builder;
}
public final LoadedSnapshot takeNPSSnapshot(File directory) throws IOException, NoDataAvailableException {
if (builder == null) throw new IllegalStateException("Builder is null"); // NOI18N
long time = System.currentTimeMillis();
CPUResultsSnapshot snapshot = builder.createSnapshot(time);
LoadedSnapshot ls = new LoadedSnapshot(snapshot, ProfilingSettingsPresets.createCPUPreset(), null, null);
File file = Utils.getUniqueFile(directory,
ResultsManager.getDefault().getDefaultSnapshotFileName(ls),
"." + ResultsManager.SNAPSHOT_EXTENSION); // NOI18N
DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
try {
ls.save(dos);
ls.setFile(file);
ls.setSaved(true);
} finally {
dos.close();
}
return ls;
}
public abstract void takeSnapshot(boolean openView);
}
}