/*
* 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.profiler;
import com.sun.tools.visualvm.application.Application;
import com.sun.tools.visualvm.application.jvm.Jvm;
import com.sun.tools.visualvm.application.jvm.JvmFactory;
import com.sun.tools.visualvm.core.datasource.descriptor.DataSourceDescriptor;
import com.sun.tools.visualvm.core.datasource.descriptor.DataSourceDescriptorFactory;
import com.sun.tools.visualvm.core.datasupport.DataChangeEvent;
import com.sun.tools.visualvm.core.datasupport.DataChangeListener;
import com.sun.tools.visualvm.core.datasupport.Stateful;
import com.sun.tools.visualvm.core.ui.DataSourceView;
import com.sun.tools.visualvm.core.ui.DataSourceWindowManager;
import com.sun.tools.visualvm.host.Host;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.swing.SwingUtilities;
import org.netbeans.lib.profiler.common.Profiler;
import org.netbeans.lib.profiler.common.ProfilingSettings;
import org.netbeans.lib.profiler.common.SessionSettings;
import org.netbeans.lib.profiler.global.Platform;
import org.netbeans.modules.profiler.NetBeansProfiler;
import org.netbeans.modules.profiler.ProfilerControlPanel2;
import org.netbeans.modules.profiler.api.ProfilerIDESettings;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
/**
*
* @author Jiri Sedlacek
*/
public final class ProfilerSupport {
// private static final Logger LOGGER = Logger.getLogger(ProfilerSupport.class.getName());
private static final boolean FORCE_PROFILING_SUPPORTED =
Boolean.getBoolean("com.sun.tools.visualvm.profiler.SupportAllVMs"); // NOI18N
private static final String HOTSPOT_VM_NAME_PREFIX = "Java HotSpot"; // NOI18N
private static final String OPENJDK_VM_NAME_PREFIX = "OpenJDK "; // NOI18N
private static final String SAPJDK_VM_NAME_PREFIX = "SAP Java "; // NOI18N
private static final String SUN_VM_VENDOR_PREFIX = "Sun "; // NOI18N
private static final String ORACLE_VM_VENDOR_PREFIX = "Oracle "; // NOI18N
private static final String APPLE_VM_VENDOR_PREFIX = "Apple "; // NOI18N
private static final String HP_VM_VENDOR_PREFIX = "\"Hewlett-Packard "; // NOI18N
private static final String AZUL_VM_VENDOR_PREFIX = "Azul "; // NOI18N
private static final String SAP_VM_VENDOR_PREFIX = "SAP AG"; // NOI18N
private static final String JAVA_RT_16_PREFIX = "1.6.0"; // NOI18N
private static final String JAVA_RT_17_PREFIX = "1.7.0"; // NOI18N
private static final String JAVA_RT_18_PREFIX = "1.8.0"; // NOI18N
private static final String JAVA_RT_19_PREFIX = "1.9.0"; // NOI18N
private static final String JAVA_RT_9_PREFIX = "9"; // NOI18N
private static ProfilerSupport instance;
private boolean isInitialized;
private Application profiledApplication;
private final ApplicationProfilerViewProvider profilerViewProvider;
public static synchronized ProfilerSupport getInstance() {
if (instance == null) instance = new ProfilerSupport();
return instance;
}
boolean isInitialized() {
return isInitialized;
}
public String getProfiledApplicationName() {
String name = NbBundle.getMessage(ProfilerSupport.class, "STR_Externaly_started_app"); // NOI18N
Application a = getProfiledApplication();
if (a == null) {
int state = NetBeansProfiler.getDefaultNB().getProfilingState();
return state == NetBeansProfiler.PROFILING_INACTIVE ? null : name;
}
DataSourceDescriptor d = DataSourceDescriptorFactory.getDescriptor(a);
return d != null ? d.getName() : name;
}
public int getDefaultPort() {
return ProfilerIDESettings.getInstance().getPortNo();
}
public boolean hasSupportedJavaPlatforms() {
for (int i = 0; i < 5; i++) {
String code = "jdk1" + (5 + i); // NOI18N
if (supportsProfiling(code, 32) || supportsProfiling(code, 64)) return true;
}
return false;
}
public String[][] getSupportedJavaPlatforms() {
List<String> codesl = new ArrayList();
for (int i = 0; i < 5; i++) {
String code = "jdk1" + (5 + i); // NOI18N
if (supportsProfiling(code, 32) || supportsProfiling(code, 64)) codesl.add(code);
}
String[] names = new String[codesl.size()];
String[] codes = new String[codesl.size()];
String current = null;
for (int i = 0; i < codesl.size(); i++) {
codes[i] = codesl.get(i);
names[i] = getJavaName(codes[i]);
if (Platform.getJDKVersionString().equals(codes[i])) current = names[i];
}
return new String[][] { names, codes, { current } };
}
public String[][] getSupportedArchitectures(String java) {
List<String> codesl = new ArrayList();
if (supportsProfiling(java, 32)) codesl.add(Integer.toString(32));
if (supportsProfiling(java, 64)) codesl.add(Integer.toString(64));
String[] names = new String[codesl.size()];
String[] codes = new String[codesl.size()];
String current = null;
for (int i = 0; i < codesl.size(); i++) {
codes[i] = codesl.get(i);
names[i] = getArchName(Integer.parseInt(codes[i]));
if (Integer.toString(Platform.getSystemArchitecture()).equals(codes[i])) current = names[i];
}
return new String[][] { names, codes, { current } };
}
static String getJavaName(String code) {
if (Platform.JDK_15_STRING.equals(code))
return NbBundle.getMessage(ProfilerSupport.class, "STR_Java_platform_name", 5); // NOI18N
if (Platform.JDK_16_STRING.equals(code))
return NbBundle.getMessage(ProfilerSupport.class, "STR_Java_platform_name", 6); // NOI18N
if (Platform.JDK_17_STRING.equals(code))
return NbBundle.getMessage(ProfilerSupport.class, "STR_Java_platform_name", 7); // NOI18N
if (Platform.JDK_18_STRING.equals(code))
return NbBundle.getMessage(ProfilerSupport.class, "STR_Java_platform_name", 8); // NOI18N
if (Platform.JDK_19_STRING.equals(code))
return NbBundle.getMessage(ProfilerSupport.class, "STR_Java_platform_name", 9); // NOI18N
throw new IllegalArgumentException("Unknown java code " + code); // NOI18N
}
static String getArchName(int arch) {
if (32 == arch) return NbBundle.getMessage(ProfilerSupport.class, "STR_Java_arch_name", 32); // NOI18N
if (64 == arch) return NbBundle.getMessage(ProfilerSupport.class, "STR_Java_arch_name", 64); // NOI18N
throw new IllegalArgumentException("Unsupported architecture " + arch); // NOI18N
}
public boolean supportsProfiling(String java, int architecture) {
String ld = Profiler.getDefault().getLibsDir();
String nativeLib = Platform.getAgentNativeLibFullName(ld, false, java, architecture);
return new File(nativeLib).isFile();
}
public String getStartupParameter(String java, int architecture, int port) {
String ld = Profiler.getDefault().getLibsDir();
if (ld.contains(" ")) ld = "\"" + ld + "\""; // NOI18N
String nativeLib = Platform.getAgentNativeLibFullName(ld, false, java, architecture);
if (nativeLib.contains(" ")) nativeLib = "\"" + nativeLib + "\""; // NOI18N
return "-agentpath:" + nativeLib + "=" + ld + "," + port; // NOI18N
}
public void profileProcessStartup(final String java, final int architecture, final int port,
final CPUSettingsSupport cpuSettings,
final MemorySettingsSupport memorySettings,
final boolean isCPUProfiling) {
if (!CalibrationSupport.checkCalibration(java, architecture, null, null)) return;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// prevent deadlock, similar to issue #570
ProfilerControlPanel2.getDefault();
// Perform the actual attach
RequestProcessor.getDefault().post(new Runnable() {
public void run() {
if (!checkStartedApp(port)) return;
final RequestProcessor processor = new RequestProcessor("Startup Profiler @ " + port); // NOI18N
Host.LOCALHOST.getRepository().addDataChangeListener(
new DataChangeListener<Application>() {
public void dataChanged(final DataChangeEvent<Application> event) {
final DataChangeListener listener = this;
processor.post(new Runnable() {
public void run() {
if (!event.getAdded().equals(event.getCurrent())) // filter-out initial sync event
for (Application a : event.getAdded()) {
if (isProfiledApplication(a, port)) {
Host.LOCALHOST.getRepository().removeDataChangeListener(listener);
setProfiledApplication(a);
selectProfilerView(a, cpuSettings, memorySettings);
break;
}
}
}
});
}
}, Application.class);
ProfilingSettings ps = createProfilingSettings(cpuSettings, memorySettings, isCPUProfiling);
SessionSettings ss = createSessionSettings(java, architecture, port);
NetBeansProfiler.getDefaultNB().connectToStartedApp(ps, ss);
resetTerminateDialogs();
}
});
}
});
}
private static boolean checkStartedApp(int port) {
String homeDir = System.getProperty("user.home"); // NOI18N
File agentF = new File(homeDir + File.separator + ".nbprofiler" + File.separator + port); // NOI18N
if (!agentF.isFile()) return true;
String caption = NbBundle.getMessage(ProfilerSupport.class, "CAP_Warning"); // NOI18N
String message = NbBundle.getMessage(ProfilerSupport.class, "MSG_StartedTooSoon"); // NOI18N
NotifyDescriptor nd = new NotifyDescriptor(message, caption, NotifyDescriptor.OK_CANCEL_OPTION,
NotifyDescriptor.WARNING_MESSAGE, null, NotifyDescriptor.OK_OPTION);
if (DialogDisplayer.getDefault().notify(nd) != NotifyDescriptor.OK_OPTION) return false;
return checkStartedApp(port);
}
private static void resetTerminateDialogs() {
String dnsaKey = "NetBeansProfiler.handleShutdown.noResults"; // NOI18N
ProfilerIDESettings.getInstance().setDoNotShowAgain(dnsaKey, null);
dnsaKey = "NetBeansProfiler.handleShutdown"; // NOI18N
String dnsa = ProfilerIDESettings.getInstance().getDoNotShowAgain(dnsaKey);
if ("NO_OPTION".equals(dnsa)) ProfilerIDESettings.getInstance().setDoNotShowAgain(dnsaKey, null); // NOI18N }
}
private static ProfilingSettings createProfilingSettings(CPUSettingsSupport cpuSettings,
MemorySettingsSupport memorySettings, boolean isCPUProfiling) {
return isCPUProfiling ? cpuSettings.getSettings() : memorySettings.getSettings();
}
private static SessionSettings createSessionSettings(String java, int architecture, int port) {
SessionSettings ss = new SessionSettings();
ss.setJavaVersionString(java);
ss.setSystemArchitecture(architecture);
ss.setPortNo(port);
ss.setJavaExecutable(JavaInfo.getCurrentJDKExecutable()); // Workaround for calibration check, not used for profiling
return ss;
}
private static boolean isProfiledApplication(Application a, int port) {
Jvm jvm = JvmFactory.getJVMFor(a);
if (!jvm.isBasicInfoSupported()) return false;
String args = jvm.getJvmArgs();
return args.contains("-agentpath:") && args.contains("," + port); // NOI18N
}
boolean supportsProfiling(Application application) {
// Application already being profiled (Startup Profiler)
if (application == getProfiledApplication()) return true;
// Remote profiling is not supported
if (application.getHost() != Host.LOCALHOST) return false;
// Profiling current VisualVM instance is not supported
if (Application.CURRENT_APPLICATION.equals(application)) return false;
// Profiled application has to be running
if (application.getState() != Stateful.STATE_AVAILABLE) return false;
Jvm jvm = JvmFactory.getJVMFor(application);
// Basic info has to be supported and VM has to be attachable
if (!jvm.isBasicInfoSupported() || !jvm.isAttachable()) return false;
// User explicitly requests to profile any VM
if (FORCE_PROFILING_SUPPORTED) return true;
// Profiled application needs to be running JDK 6.0 or 7.0 or 8.0 or 9.0
if (!jvm.is16() && !jvm.is17() && !jvm.is18() && !jvm.is19()) return false;
String vmName = jvm.getVmName();
String vmVendor = jvm.getVmVendor();
// VM has to be a HotSpot VM or OpenJDK by Sun Microsystems Inc. or
// Oracle Co. or Apple Inc. or Hewlett-Packard Co. or
// Azul Systems, Inc. or SAP AG
return vmName != null && (vmName.startsWith(HOTSPOT_VM_NAME_PREFIX) || vmName.startsWith(OPENJDK_VM_NAME_PREFIX)
|| vmName.startsWith(SAPJDK_VM_NAME_PREFIX)) &&
vmVendor != null && (vmVendor.startsWith(ORACLE_VM_VENDOR_PREFIX) || vmVendor.startsWith(SUN_VM_VENDOR_PREFIX)
|| vmVendor.startsWith(APPLE_VM_VENDOR_PREFIX) || vmVendor.startsWith(HP_VM_VENDOR_PREFIX)
|| vmVendor.startsWith(AZUL_VM_VENDOR_PREFIX) || vmVendor.startsWith(SAP_VM_VENDOR_PREFIX));
}
static boolean classSharingBreaksProfiling(Application application) {
if (application.getState() != Stateful.STATE_AVAILABLE) return false;
Jvm jvm = JvmFactory.getJVMFor(application);
String vmInfo = jvm.getVmInfo();
boolean classSharing = vmInfo.contains("sharing"); // NOI18N
if (!jvm.isGetSystemPropertiesSupported()) return classSharing;
Properties properties = jvm.getSystemProperties();
if (properties == null) return classSharing;
String javaRTVersion = properties.getProperty("java.runtime.version"); // NOI18N
if (javaRTVersion == null) return classSharing;
int updateNumber = getUpdateNumber(javaRTVersion);
int buildNumber = getBuildNumber(javaRTVersion);
String vmName = jvm.getVmName();
// Sun JDK & derived JDKs ----------------------------------------------
if (vmName.startsWith(HOTSPOT_VM_NAME_PREFIX)) {
// JDK 6.0 is OK from Update 6 except of Update 10 Build 23 and lower
if (javaRTVersion.startsWith(JAVA_RT_16_PREFIX)) {
if (updateNumber == 10) {
if (buildNumber >= 24) return false;
} else if (updateNumber >= 6) return false;
// JDK 7.0 is OK from Build 26
} else if (javaRTVersion.startsWith(JAVA_RT_17_PREFIX)) {
if (updateNumber == 0) {
if (buildNumber >= 26) return false;
} else {
return false;
}
} else if (javaRTVersion.startsWith(JAVA_RT_18_PREFIX)) {
return false;
} else if (javaRTVersion.startsWith(JAVA_RT_19_PREFIX)) {
return false;
} else if (javaRTVersion.startsWith(JAVA_RT_9_PREFIX)) {
return false;
}
// OpenJDK -------------------------------------------------------------
} else if(vmName.startsWith(OPENJDK_VM_NAME_PREFIX)) {
// OpenJDK 6 is OK from Build 11
if (javaRTVersion.startsWith(JAVA_RT_16_PREFIX)) {
if (updateNumber == 0) {
if (buildNumber >= 11) return false;
} else {
return false;
}
// OpenJDK 7 is assumed to be OK from Build 26 (not tested)
} else if (javaRTVersion.startsWith(JAVA_RT_17_PREFIX)) {
if (updateNumber == 0) {
if (buildNumber >= 26) return false;
} else {
return false;
}
}
// OpenJDK 8 should be OK
else if (javaRTVersion.startsWith(JAVA_RT_18_PREFIX)) {
return false;
} else if (javaRTVersion.startsWith(JAVA_RT_19_PREFIX)) {
return false;
} else if (javaRTVersion.startsWith(JAVA_RT_9_PREFIX)) {
return false;
}
}
return classSharing;
}
private static int getUpdateNumber(String javaRTVersion) {
int underscoreIndex = javaRTVersion.indexOf("_"); // NOI18N
if (underscoreIndex == -1) return 0; // Assumes no update, may be incorrect for unexpected javaRTVersion format
try {
String updateNumberString = javaRTVersion.substring(underscoreIndex + "_".length(), javaRTVersion.indexOf("-")); // NOI18N
return Integer.parseInt(updateNumberString);
} catch (Exception e) {}
return -1;
}
private static int getBuildNumber(String javaRTVersion) {
try {
String buildNumberString = javaRTVersion.substring(javaRTVersion.indexOf("-b") + "-b".length()); // NOI18N
return Integer.parseInt(buildNumberString);
} catch (Exception e) {}
return -1;
}
synchronized void setProfiledApplication(Application profiledApplication) {
this.profiledApplication = profiledApplication;
}
synchronized Application getProfiledApplication() {
return profiledApplication;
}
void selectActiveProfilerView() {
selectProfilerView(getProfiledApplication());
}
void selectProfilerView(Application application) {
selectProfilerView(application, null, null);
}
private void selectProfilerView(Application application, final CPUSettingsSupport cpu, final MemorySettingsSupport memory) {
if (application == null) return;
final DataSourceView activeView = profilerViewProvider.view(application);
if (activeView == null) return;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (cpu != null || memory != null) ((ApplicationProfilerView)activeView).copySettings(cpu, memory);
DataSourceWindowManager.sharedInstance().selectView(activeView);
}
});
}
private ProfilerSupport() {
profilerViewProvider = new ApplicationProfilerViewProvider();
profilerViewProvider.initialize();
ProfilerIDESettings.getInstance().setAutoOpenSnapshot(false);
ProfilerIDESettings.getInstance().setAutoSaveSnapshot(true);
}
}