8154985: Add the ability to use main class as lookup (as jcmd) to jinfo, jmap, jstack

Reviewed-by: sla, dsamersoff
This commit is contained in:
Robbin Ehn 2016-05-10 06:52:40 +02:00
parent ae4d032d41
commit d0b67fecb3
6 changed files with 219 additions and 133 deletions
jdk/src/jdk.jcmd/share/classes/sun/tools

@ -0,0 +1,128 @@
/*
* Copyright (c) 2016, 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 sun.tools.common;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import sun.jvmstat.monitor.MonitorException;
import sun.jvmstat.monitor.MonitoredHost;
import sun.jvmstat.monitor.MonitoredVm;
import sun.jvmstat.monitor.MonitoredVmUtil;
import sun.jvmstat.monitor.VmIdentifier;
/**
* Class for finding process matching a process argument,
* excluding tool it self and returning a list containing
* the process identifiers.
*/
public class ProcessArgumentMatcher {
private String excludeCls;
private String matchClass = null;
private String singlePid = null;
private boolean matchAll = false;
public ProcessArgumentMatcher(String pidArg, Class<?> excludeClass) {
excludeCls = excludeClass.getName();
if (pidArg == null || pidArg.isEmpty()) {
throw new IllegalArgumentException("Pid string is invalid");
}
if (pidArg.charAt(0) == '-') {
throw new IllegalArgumentException("Unrecognized " + pidArg);
}
try {
long pid = Long.parseLong(pidArg);
if (pid == 0) {
matchAll = true;
} else {
singlePid = String.valueOf(pid);
}
} catch (NumberFormatException nfe) {
matchClass = pidArg;
}
}
private boolean check(VirtualMachineDescriptor vmd) {
String mainClass = null;
try {
VmIdentifier vmId = new VmIdentifier(vmd.id());
MonitoredHost monitoredHost = MonitoredHost.getMonitoredHost(vmId);
MonitoredVm monitoredVm = monitoredHost.getMonitoredVm(vmId, -1);
mainClass = MonitoredVmUtil.mainClass(monitoredVm, true);
monitoredHost.detach(monitoredVm);
} catch (NullPointerException npe) {
// There is a potential race, where a running java app is being
// queried, unfortunately the java app has shutdown after this
// method is started but before getMonitoredVM is called.
// If this is the case, then the /tmp/hsperfdata_xxx/pid file
// will have disappeared and we will get a NullPointerException.
// Handle this gracefully....
return false;
} catch (MonitorException | URISyntaxException e) {
if (e.getMessage() != null) {
System.err.println(e.getMessage());
} else {
Throwable cause = e.getCause();
if ((cause != null) && (cause.getMessage() != null)) {
System.err.println(cause.getMessage());
} else {
e.printStackTrace();
}
}
return false;
}
if (mainClass.equals(excludeCls)) {
return false;
}
if (matchAll || mainClass.indexOf(matchClass) != -1) {
return true;
}
return false;
}
public Collection<String> getPids() {
Collection<String> pids = new ArrayList<>();
if (singlePid != null) {
pids.add(singlePid);
return pids;
}
List<VirtualMachineDescriptor> vmds = VirtualMachine.list();
for (VirtualMachineDescriptor vmd : vmds) {
if (check(vmd)) {
pids.add(vmd.id());
}
}
return pids;
}
}

@ -33,16 +33,14 @@ class Arguments {
private boolean listProcesses = false;
private boolean listCounters = false;
private boolean showUsage = false;
private int pid = -1;
private String command = null;
private String processSubstring;
private String processString = null;
public boolean isListProcesses() { return listProcesses; }
public boolean isListCounters() { return listCounters; }
public boolean isShowUsage() { return showUsage; }
public int getPid() { return pid; }
public String getCommand() { return command; }
public String getProcessSubstring() { return processSubstring; }
public String getProcessString() { return processString; }
public Arguments(String[] args) {
if (args.length == 0 || args[0].equals("-l")) {
@ -55,15 +53,7 @@ class Arguments {
return;
}
try {
pid = Integer.parseInt(args[0]);
} catch (NumberFormatException ex) {
// use as a partial class-name instead
if (args[0].charAt(0) != '-') {
// unless it starts with a '-'
processSubstring = args[0];
}
}
processString = args[0];
StringBuilder sb = new StringBuilder();
for (int i = 1; i < args.length; i++) {

@ -29,22 +29,22 @@ import java.io.InputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.net.URISyntaxException;
import com.sun.tools.attach.AttachOperationFailedException;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import sun.tools.attach.HotSpotVirtualMachine;
import sun.tools.common.ProcessArgumentMatcher;
import sun.tools.jstat.JStatLogger;
import sun.jvmstat.monitor.Monitor;
import sun.jvmstat.monitor.MonitoredHost;
import sun.jvmstat.monitor.MonitoredVm;
import sun.jvmstat.monitor.MonitoredVmUtil;
import sun.jvmstat.monitor.MonitorException;
import sun.jvmstat.monitor.VmIdentifier;
@ -73,52 +73,18 @@ public class JCmd {
System.exit(0);
}
List<String> pids = new ArrayList<String>();
if (arg.getPid() == 0) {
// find all VMs
List<VirtualMachineDescriptor> vmds = VirtualMachine.list();
for (VirtualMachineDescriptor vmd : vmds) {
if (!isJCmdProcess(vmd)) {
pids.add(vmd.id());
}
}
} else if (arg.getProcessSubstring() != null) {
// use the partial class-name match
List<VirtualMachineDescriptor> vmds = VirtualMachine.list();
for (VirtualMachineDescriptor vmd : vmds) {
if (isJCmdProcess(vmd)) {
continue;
}
try {
String mainClass = getMainClass(vmd);
if (mainClass != null
&& mainClass.indexOf(arg.getProcessSubstring()) != -1) {
pids.add(vmd.id());
}
} catch (MonitorException|URISyntaxException e) {
if (e.getMessage() != null) {
System.err.println(e.getMessage());
} else {
Throwable cause = e.getCause();
if ((cause != null) && (cause.getMessage() != null)) {
System.err.println(cause.getMessage());
} else {
e.printStackTrace();
}
}
}
}
if (pids.isEmpty()) {
System.err.println("Could not find any processes matching : '"
+ arg.getProcessSubstring() + "'");
System.exit(1);
}
} else if (arg.getPid() == -1) {
Collection<String> pids = Collections.emptyList();
try {
ProcessArgumentMatcher ap = new ProcessArgumentMatcher(arg.getProcessString(), JCmd.class);
pids = ap.getPids();
} catch (IllegalArgumentException iae) {
System.err.println("Invalid pid specified");
System.exit(1);
} else {
// Use the found pid
pids.add(arg.getPid() + "");
}
if (pids.isEmpty()) {
System.err.println("Could not find any processes matching : '"
+ arg.getProcessString() + "'");
System.exit(1);
}
boolean success = true;
@ -199,36 +165,6 @@ public class JCmd {
}
}
private static boolean isJCmdProcess(VirtualMachineDescriptor vmd) {
try {
String mainClass = getMainClass(vmd);
return mainClass != null && mainClass.equals(JCmd.class.getName());
} catch (URISyntaxException|MonitorException ex) {
return false;
}
}
private static String getMainClass(VirtualMachineDescriptor vmd)
throws URISyntaxException, MonitorException {
try {
String mainClass = null;
VmIdentifier vmId = new VmIdentifier(vmd.id());
MonitoredHost monitoredHost = MonitoredHost.getMonitoredHost(vmId);
MonitoredVm monitoredVm = monitoredHost.getMonitoredVm(vmId, -1);
mainClass = MonitoredVmUtil.mainClass(monitoredVm, true);
monitoredHost.detach(monitoredVm);
return mainClass;
} catch(NullPointerException e) {
// There is a potential race, where a running java app is being
// queried, unfortunately the java app has shutdown after this
// method is started but before getMonitoredVM is called.
// If this is the case, then the /tmp/hsperfdata_xxx/pid file
// will have disappeared and we will get a NullPointerException.
// Handle this gracefully....
return null;
}
}
/**
* Class to compare two Monitor objects by name in ascending order.
* (from jstat)

@ -25,13 +25,14 @@
package sun.tools.jinfo;
import java.util.Arrays;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import com.sun.tools.attach.VirtualMachine;
import sun.tools.attach.HotSpotVirtualMachine;
import sun.tools.common.ProcessArgumentMatcher;
/*
* This class is the main class for the JInfo utility. It parses its arguments
@ -82,28 +83,49 @@ final public class JInfo {
// Next we check the parameter count. -flag allows extra parameters
int paramCount = args.length - optionCount;
if ((doFlag && paramCount != 2) || (paramCount != 1)) {
if ((doFlag && paramCount != 2) || ((!doFlag && paramCount != 1))) {
usage(1);
}
if (!doFlag && !doFlags && !doSysprops) {
// Print flags and sysporps if no options given
sysprops(args[optionCount]);
System.out.println();
flags(args[optionCount]);
System.out.println();
commandLine(args[optionCount]);
ProcessArgumentMatcher ap = new ProcessArgumentMatcher(args[optionCount], JInfo.class);
Collection<String> pids = ap.getPids();
for (String pid : pids) {
if (pids.size() > 1) {
System.out.println("Pid:" + pid);
}
sysprops(pid);
System.out.println();
flags(pid);
System.out.println();
commandLine(pid);
}
}
if (doFlag) {
flag(args[optionCount+1], args[optionCount]);
}
else {
if (doFlags) {
flags(args[optionCount]);
ProcessArgumentMatcher ap = new ProcessArgumentMatcher(args[optionCount+1], JInfo.class);
Collection<String> pids = ap.getPids();
for (String pid : pids) {
if (pids.size() > 1) {
System.out.println("Pid:" + pid);
}
flag(pid, args[optionCount]);
}
else if (doSysprops) {
sysprops(args[optionCount]);
}
else if (doFlags || doSysprops) {
ProcessArgumentMatcher ap = new ProcessArgumentMatcher(args[optionCount], JInfo.class);
Collection<String> pids = ap.getPids();
for (String pid : pids) {
if (pids.size() > 1) {
System.out.println("Pid:" + pid);
}
if (doFlags) {
flags(pid);
}
else if (doSysprops) {
sysprops(pid);
}
}
}
}
@ -193,24 +215,23 @@ final public class JInfo {
private static void checkForUnsupportedOptions(String[] args) {
// Check arguments for -F, and non-numeric value
// and warn the user that SA is not supported anymore
int maxCount = 1;
int paramCount = 0;
for (String s : args) {
if (s.equals("-F")) {
SAOptionError("-F option used");
}
if (s.equals("-flag")) {
maxCount = 2;
}
if (! s.startsWith("-")) {
if (! s.matches("[0-9]+")) {
SAOptionError("non PID argument");
}
paramCount += 1;
}
}
if (paramCount > 1) {
SAOptionError("More than one non-option argument");
if (paramCount > maxCount) {
SAOptionError("More than " + maxCount + " non-option argument");
}
}

@ -29,10 +29,12 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.AttachNotSupportedException;
import sun.tools.attach.HotSpotVirtualMachine;
import sun.tools.common.ProcessArgumentMatcher;
/*
* This class is the main class for the JMap utility. It parses its arguments
@ -83,22 +85,29 @@ public class JMap {
usage(1);
}
String pid = args[1];
String pidArg = args[1];
// Here we handle the built-in options
// As more options are added we should create an abstract tool class and
// have a table to map the options
if (option.equals("-histo")) {
histo(pid, "");
} else if (option.startsWith("-histo:")) {
histo(pid, option.substring("-histo:".length()));
} else if (option.startsWith("-dump:")) {
dump(pid, option.substring("-dump:".length()));
} else if (option.equals("-finalizerinfo")) {
executeCommandForPid(pid, "jcmd", "GC.finalizer_info");
} else if (option.equals("-clstats")) {
executeCommandForPid(pid, "jcmd", "GC.class_stats");
} else {
usage(1);
ProcessArgumentMatcher ap = new ProcessArgumentMatcher(pidArg, JMap.class);
Collection<String> pids = ap.getPids();
for (String pid : pids) {
if (pids.size() > 1) {
System.out.println("Pid:" + pid);
}
if (option.equals("-histo")) {
histo(pid, "");
} else if (option.startsWith("-histo:")) {
histo(pid, option.substring("-histo:".length()));
} else if (option.startsWith("-dump:")) {
dump(pid, option.substring("-dump:".length()));
} else if (option.equals("-finalizerinfo")) {
executeCommandForPid(pid, "jcmd", "GC.finalizer_info");
} else if (option.equals("-clstats")) {
executeCommandForPid(pid, "jcmd", "GC.class_stats");
} else {
usage(1);
}
}
}
@ -204,9 +213,6 @@ public class JMap {
*/
if (! s.startsWith("-")) {
if (! s.matches("[0-9]+")) {
SAOptionError("non PID argument");
}
paramCount += 1;
}
}

@ -26,10 +26,11 @@
package sun.tools.jstack;
import java.io.InputStream;
import java.util.Collection;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.AttachNotSupportedException;
import sun.tools.attach.HotSpotVirtualMachine;
import sun.tools.common.ProcessArgumentMatcher;
/*
* This class is the main class for the JStack utility. It parses its arguments
@ -74,14 +75,21 @@ public class JStack {
}
// pass -l to thread dump operation to get extra lock info
String pid = args[optionCount];
String pidArg = args[optionCount];
String params[];
if (locks) {
params = new String[] { "-l" };
} else {
params = new String[0];
}
runThreadDump(pid, params);
ProcessArgumentMatcher ap = new ProcessArgumentMatcher(pidArg, JStack.class);
Collection<String> pids = ap.getPids();
for (String pid : pids) {
if (pids.size() > 1) {
System.out.println("Pid:" + pid);
}
runThreadDump(pid, params);
}
}
// Attach to pid and perform a thread dump
@ -133,9 +141,6 @@ public class JStack {
}
if (! s.startsWith("-")) {
if (! s.matches("[0-9]+")) {
SAOptionError("non PID argument");
}
paramCount += 1;
}
}