/*
* Copyright (c) 2007, 2013, 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.jvm;
import com.sun.tools.visualvm.application.jvm.Jvm;
import com.sun.tools.visualvm.application.jvm.MonitoredData;
import com.sun.tools.visualvm.application.jvm.MonitoredDataListener;
import com.sun.tools.visualvm.application.Application;
import com.sun.tools.visualvm.application.jvm.HeapHistogram;
import com.sun.tools.visualvm.core.datasupport.Stateful;
import com.sun.tools.visualvm.heapdump.HeapDumpSupport;
import com.sun.tools.visualvm.host.Host;
import com.sun.tools.visualvm.threaddump.ThreadDumpSupport;
import com.sun.tools.visualvm.tools.attach.AttachModel;
import com.sun.tools.visualvm.tools.attach.AttachModelFactory;
import com.sun.tools.visualvm.tools.jmx.JmxModel;
import com.sun.tools.visualvm.tools.jmx.JmxModelFactory;
import com.sun.tools.visualvm.tools.jmx.JvmMXBeans;
import com.sun.tools.visualvm.tools.jvmstat.JvmstatModel;
import com.sun.tools.visualvm.tools.jvmstat.JvmstatListener;
import com.sun.tools.visualvm.tools.jvmstat.JvmJvmstatModel;
import com.sun.tools.visualvm.tools.jvmstat.JvmJvmstatModelFactory;
import com.sun.tools.visualvm.tools.sa.SaModel;
import com.sun.tools.visualvm.tools.sa.SaModelFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.openide.util.NbBundle;
/**
*
* @author Tomas Hurka
*/
public class JVMImpl extends Jvm implements JvmstatListener {
private static final String HEAP_DUMP_ON_OOME = "HeapDumpOnOutOfMemoryError"; // NOI18N
private static final String HEAP_DUMP_PATH = "HeapDumpPath"; // NOI18N
Application application;
Boolean isDumpOnOOMEnabled;
JvmstatModel monitoredVm;
JvmJvmstatModel jvmstatModel;
Set<MonitoredDataListener> listeners;
JmxSupport jmxSupport;
// static JVM data
private boolean staticDataInitialized;
private Object staticDataLock = new Object();
private String commandLine;
private String jvmArgs;
private String jvmFlags;
private String mainArgs;
private String mainClass;
private String vmVersion;
private String javaVersion;
private String javaHome;
private String vmInfo;
private String vmName;
private String vmVendor;
JVMImpl(Application app,JvmstatModel jvms) {
application = app;
monitoredVm = jvms;
jvmstatModel = JvmJvmstatModelFactory.getJvmstatModelFor(app);
jmxSupport = new JmxSupport(app,this);
listeners = new HashSet();
}
JVMImpl(Application app) {
application = app;
jmxSupport = new JmxSupport(app,this);
listeners = new HashSet();
}
public boolean isAttachable() {
if (jvmstatModel != null) {
if (!jvmstatModel.isAttachable()) {
return false;
}
return getAttach() != null;
}
return false;
}
public boolean isBasicInfoSupported() {
return true;
}
public String getCommandLine() {
initStaticData();
return commandLine;
}
public String getJvmArgs() {
initStaticData();
return jvmArgs;
}
public String getJvmFlags() {
initStaticData();
return jvmFlags;
}
public String getMainArgs() {
initStaticData();
return mainArgs;
}
public String getMainClass() {
initStaticData();
return mainClass;
}
public String getVmVersion() {
initStaticData();
return vmVersion;
}
public String getJavaVersion() {
initStaticData();
if (javaVersion != null) {
return javaVersion;
}
return vmVersion;
}
public String getJavaHome() {
initStaticData();
return javaHome;
}
public String getVmInfo() {
initStaticData();
return vmInfo;
}
public String getVmName() {
initStaticData();
return vmName;
}
public String getVmVendor() {
initStaticData();
return vmVendor;
}
public boolean is14() {
String ver = getVmVersion();
if (ver != null && ver.startsWith("1.4.")) { // NOI18N
return true;
}
return false;
}
public boolean is15() {
String ver = getJavaVersion();
if (ver != null && ver.startsWith("1.5.")) { // NOI18N
return true;
}
return false;
}
public boolean is16() {
String ver = getJavaVersion();
if (ver != null && (ver.startsWith("1.6.") || ver.startsWith("10.") || ver.startsWith("11."))) { // NOI18N
return true;
}
return false;
}
public boolean is17() {
String ver = getJavaVersion();
if (ver != null && (ver.startsWith("1.7.") || ver.startsWith("12.") || ver.startsWith("13.") || ver.startsWith("14."))) { // NOI18N
return true;
}
return false;
}
public boolean is18() {
String ver = getJavaVersion();
if (ver != null && ver.startsWith("1.8.")) {
return true;
}
return false;
}
public boolean is19() {
String ver = getJavaVersion();
if (ver != null && (ver.startsWith("1.9.") || (ver.startsWith("9")))) { // NOI18N
return true;
}
return false;
}
public boolean isDumpOnOOMEnabled() {
if (isDumpOnOOMEnabled == null) {
AttachModel attach = getAttach();
String args = null;
if (attach != null) {
args = attach.printFlag(HEAP_DUMP_ON_OOME);
}
if (args == null) {
JmxModel jmx = getJmxModel();
if (jmx != null && jmx.isTakeHeapDumpSupported()) {
String value = jmx.getFlagValue(HEAP_DUMP_ON_OOME);
isDumpOnOOMEnabled = Boolean.valueOf(value);
return isDumpOnOOMEnabled.booleanValue();
}
}
if (args == null && monitoredVm != null) {
args = getJvmFlags().concat(getJvmArgs());
}
if (args != null && args.contains("-XX:+"+HEAP_DUMP_ON_OOME)) { // NOI18N
isDumpOnOOMEnabled = Boolean.TRUE;
} else {
isDumpOnOOMEnabled = Boolean.FALSE;
}
}
return isDumpOnOOMEnabled.booleanValue();
}
public void addMonitoredDataListener(MonitoredDataListener l) {
synchronized (listeners) {
if (listeners.add(l)) {
if (monitoredVm != null) {
if (jmxSupport != null) jmxSupport.disableTimer();
monitoredVm.addJvmstatListener(this);
} else {
if (jmxSupport != null) jmxSupport.initTimer();
}
}
}
}
public void removeMonitoredDataListener(MonitoredDataListener l) {
synchronized (listeners) {
if (listeners.remove(l)) {
if (listeners.isEmpty()) {
if (monitoredVm != null) {
monitoredVm.removeJvmstatListener(this);
} else {
if (jmxSupport != null) jmxSupport.disableTimer();
}
}
}
}
}
public String[] getGenName() {
if (jvmstatModel != null) {
return jvmstatModel.getGenName();
}
if (jmxSupport != null) {
return jmxSupport.getGenName();
}
throw new UnsupportedOperationException();
}
public boolean isMonitoringSupported() {
return isClassMonitoringSupported() || isThreadMonitoringSupported() || isMemoryMonitoringSupported();
}
public boolean isClassMonitoringSupported() {
return monitoredVm != null || jmxSupport.getRuntime() != null;
}
public boolean isThreadMonitoringSupported() {
return (!is14() && monitoredVm != null) || jmxSupport.getRuntime() != null;
}
public boolean isMemoryMonitoringSupported() {
return monitoredVm != null || jmxSupport.getRuntime() != null;
}
public boolean isGetSystemPropertiesSupported() {
return getAttach() != null || jmxSupport.getRuntime() != null || getSAAgent() != null;
}
public Properties getSystemProperties() {
AttachModel attach = getAttach();
Properties prop = null;
if (attach != null) {
prop = attach.getSystemProperties();
}
if (prop != null)
return prop;
JmxModel jmx = getJmxModel();
if (jmx != null) {
prop = jmx.getSystemProperties();
}
if (prop != null) {
return prop;
}
SaModel saAgent = getSAAgent();
if (saAgent != null) {
return saAgent.getSystemProperties();
}
if (!isGetSystemPropertiesSupported()) {
throw new UnsupportedOperationException();
}
return null;
}
public boolean isDumpOnOOMEnabledSupported() {
if (getAttach() != null) {
return true;
}
JmxModel jmx = getJmxModel();
if (Host.LOCALHOST.equals(application.getHost()) && jmx != null && jmx.isTakeHeapDumpSupported()) {
return true;
}
return false;
}
public synchronized void setDumpOnOOMEnabled(boolean enabled) {
if (!isDumpOnOOMEnabledSupported()) {
throw new UnsupportedOperationException();
}
AttachModel attach = getAttach();
if (attach!=null) {
attach.setFlag(HEAP_DUMP_ON_OOME,enabled?"1":"0"); // NOI18N
if (enabled) {
attach.setFlag(HEAP_DUMP_PATH,application.getStorage().getDirectory().getAbsolutePath());
}
} else {
JmxModel jmx = getJmxModel();
jmx.setFlagValue(HEAP_DUMP_ON_OOME,Boolean.toString(enabled));
if (enabled) {
jmx.setFlagValue(HEAP_DUMP_PATH,application.getStorage().getDirectory().getAbsolutePath());
}
}
Boolean oldVlue = isDumpOnOOMEnabled;
isDumpOnOOMEnabled = Boolean.valueOf(enabled);
firePropertyChange(PROPERTY_DUMP_OOME_ENABLED,oldVlue,isDumpOnOOMEnabled);
}
public boolean isTakeHeapDumpSupported() {
if (getAttach() != null) {
return true;
}
JmxModel jmx = getJmxModel();
if (Host.LOCALHOST.equals(application.getHost()) && jmx != null && jmx.isTakeHeapDumpSupported()) {
return true;
}
return false;
}
public File takeHeapDump() throws IOException {
if (!isTakeHeapDumpSupported()) {
throw new UnsupportedOperationException();
}
File snapshotDir = application.getStorage().getDirectory();
String name = HeapDumpSupport.getInstance().getCategory().createFileName();
File dumpFile = new File(snapshotDir,name);
AttachModel attach = getAttach();
if (attach != null) {
if (attach.takeHeapDump(dumpFile.getAbsolutePath())) {
return dumpFile;
}
return null;
}
if (getJmxModel().takeHeapDump(dumpFile.getAbsolutePath())) {
return dumpFile;
}
return null;
}
public boolean isTakeThreadDumpSupported() {
if (getAttach() != null) {
return true;
}
JmxModel jmx = getJmxModel();
if (jmx != null && jmx.isTakeThreadDumpSupported()) {
return true;
}
return getSAAgent() != null;
}
public File takeThreadDump() throws IOException {
AttachModel attach = getAttach();
String threadDump = null;
if (attach != null) {
threadDump = attach.takeThreadDump();
}
if (threadDump == null) {
JmxModel jmx = getJmxModel();
if (jmx != null) {
threadDump = jmx.takeThreadDump();
}
}
if (threadDump == null) {
SaModel sa = getSAAgent();
if (sa != null) {
threadDump = sa.takeThreadDump();
}
}
if (threadDump == null) {
if (!isTakeThreadDumpSupported()) {
throw new UnsupportedOperationException();
}
threadDump = NbBundle.getMessage(JVMImpl.class, "MSG_ThreadDumpfailed"); // NOI18N
}
File snapshotDir = application.getStorage().getDirectory();
String name = ThreadDumpSupport.getInstance().getCategory().createFileName();
File dumpFile = new File(snapshotDir,name);
OutputStream os = new FileOutputStream(dumpFile);
os.write(threadDump.getBytes("UTF-8")); // NOI18N
os.close();
return dumpFile;
}
public HeapHistogram takeHeapHistogram() {
AttachModel attach = getAttach();
HeapHistogram histogram = null;
if (attach != null) {
histogram = attach.takeHeapHistogram();
}
if (histogram == null) {
JmxModel jmx = getJmxModel();
if (jmx != null) {
histogram = jmx.takeHeapHistogram();
}
}
return histogram;
}
public boolean isCpuMonitoringSupported() {
return jmxSupport.hasProcessCPUTimeAttribute();
}
public boolean isCollectionTimeSupported() {
Collection gcList = jmxSupport.getGarbageCollectorMXBeans();
return gcList != null && !gcList.isEmpty();
}
public MonitoredData getMonitoredData() {
if (application.getState() == Stateful.STATE_AVAILABLE) {
if (monitoredVm != null) {
return new MonitoredDataImpl(jvmstatModel,jmxSupport);
} else if (jmxSupport != null) {
JvmMXBeans jmx = jmxSupport.getJvmMXBeans();
if (jmx != null) {
return new MonitoredDataImpl(jmxSupport,jmx);
}
}
}
return null;
}
protected AttachModel getAttach() {
return AttachModelFactory.getAttachFor(application);
}
protected SaModel getSAAgent() {
return SaModelFactory.getSAAgentFor(application);
}
protected JmxModel getJmxModel() {
return JmxModelFactory.getJmxModelFor(application);
}
protected void initStaticData() {
synchronized (staticDataLock) {
if (staticDataInitialized) {
return;
}
if (jvmstatModel != null) {
commandLine = jvmstatModel.getCommandLine();
jvmArgs = getJvmArgsJvmstat();
jvmFlags = jvmstatModel.getJvmFlags();
mainArgs = jvmstatModel.getMainArgs();
mainClass = jvmstatModel.getMainClass();
vmVersion = jvmstatModel.getVmVersion();
javaVersion = monitoredVm.findByName("java.property.java.version"); // NOI18N
javaHome = jvmstatModel.getJavaHome();
vmInfo = jvmstatModel.getVmInfo();
vmName = jvmstatModel.getVmName();
vmVendor = jvmstatModel.getVmVendor();
} else {
jvmArgs = jmxSupport.getJvmArgs();
Properties prop = getJmxModel().getSystemProperties();
if (prop != null) {
vmVersion = prop.getProperty("java.vm.version"); // NOI18N
javaVersion = prop.getProperty("java.version"); // NOI18N
javaHome = prop.getProperty("java.home"); // NOI18N
vmInfo = prop.getProperty("java.vm.info"); // NOI18N
vmName = prop.getProperty("java.vm.name"); // NOI18N
vmVendor = prop.getProperty("java.vm.vendor"); // NOI18N
}
}
staticDataInitialized = true;
}
}
private String getJvmArgsJvmstat() {
String args = jvmstatModel.getJvmArgs();
if (args.length() == 1024) {
args = jmxSupport.getJvmArgs();
if (args == null) {
SaModel sa = getSAAgent();
if (sa != null) {
args = sa.getJvmArgs();
}
}
if (args == null || args.length() == 0) {
args = jvmstatModel.getJvmArgs();
}
}
return args;
}
public void dataChanged(JvmstatModel stat) {
assert stat == monitoredVm;
MonitoredData data = new MonitoredDataImpl(jvmstatModel,jmxSupport);
notifyListeners(data);
}
void notifyListeners(final MonitoredData data) {
List<MonitoredDataListener> listenersCopy;
synchronized (listeners) {
listenersCopy = new ArrayList(listeners);
}
for (MonitoredDataListener listener : listenersCopy) {
listener.monitoredDataEvent(data);
}
}
}