1/* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package sun.tools.common; 27 28import java.net.URISyntaxException; 29import java.util.ArrayList; 30import java.util.Collection; 31import java.util.List; 32import java.util.stream.Collectors; 33 34import com.sun.tools.attach.VirtualMachine; 35import com.sun.tools.attach.VirtualMachineDescriptor; 36 37import sun.jvmstat.monitor.MonitorException; 38import sun.jvmstat.monitor.MonitoredHost; 39import sun.jvmstat.monitor.MonitoredVm; 40import sun.jvmstat.monitor.MonitoredVmUtil; 41import sun.jvmstat.monitor.VmIdentifier; 42 43/** 44 * Class for finding process matching a process argument, 45 * excluding tool it self and returning a list containing 46 * the process identifiers. 47 */ 48public class ProcessArgumentMatcher { 49 private String matchClass; 50 private String singlePid; 51 52 public ProcessArgumentMatcher(String pidArg) { 53 if (pidArg == null || pidArg.isEmpty()) { 54 throw new IllegalArgumentException("Pid string is invalid"); 55 } 56 if (pidArg.charAt(0) == '-') { 57 throw new IllegalArgumentException("Unrecognized " + pidArg); 58 } 59 try { 60 long pid = Long.parseLong(pidArg); 61 if (pid != 0) { 62 singlePid = String.valueOf(pid); 63 } 64 } catch (NumberFormatException nfe) { 65 matchClass = pidArg; 66 } 67 } 68 69 private static String getExcludeStringFrom(Class<?> excludeClass) { 70 if (excludeClass == null) { 71 return ""; 72 } 73 Module m = excludeClass.getModule(); 74 if (m.isNamed()) { 75 return m.getName() + "/" + excludeClass.getName(); 76 } 77 return excludeClass.getName(); 78 } 79 80 private static boolean check(VirtualMachineDescriptor vmd, String excludeClass, String partialMatch) { 81 String mainClass = null; 82 try { 83 VmIdentifier vmId = new VmIdentifier(vmd.id()); 84 MonitoredHost monitoredHost = MonitoredHost.getMonitoredHost(vmId); 85 MonitoredVm monitoredVm = monitoredHost.getMonitoredVm(vmId, -1); 86 mainClass = MonitoredVmUtil.mainClass(monitoredVm, true); 87 monitoredHost.detach(monitoredVm); 88 } catch (NullPointerException npe) { 89 // There is a potential race, where a running java app is being 90 // queried, unfortunately the java app has shutdown after this 91 // method is started but before getMonitoredVM is called. 92 // If this is the case, then the /tmp/hsperfdata_xxx/pid file 93 // will have disappeared and we will get a NullPointerException. 94 // Handle this gracefully.... 95 return false; 96 } catch (MonitorException | URISyntaxException e) { 97 return false; 98 } 99 100 if (excludeClass != null && mainClass.equals(excludeClass)) { 101 return false; 102 } 103 104 if (partialMatch != null && mainClass.indexOf(partialMatch) == -1) { 105 return false; 106 } 107 108 return true; 109 } 110 111 private static Collection<VirtualMachineDescriptor> getSingleVMD(String pid) { 112 Collection<VirtualMachineDescriptor> vids = new ArrayList<>(); 113 List<VirtualMachineDescriptor> vmds = VirtualMachine.list(); 114 for (VirtualMachineDescriptor vmd : vmds) { 115 if (check(vmd, null, null)) { 116 if (pid.equals(vmd.id())) { 117 vids.add(vmd); 118 } 119 } 120 } 121 return vids; 122 } 123 124 private static Collection<VirtualMachineDescriptor> getVMDs(Class<?> excludeClass, String partialMatch) { 125 String excludeCls = getExcludeStringFrom(excludeClass); 126 Collection<VirtualMachineDescriptor> vids = new ArrayList<>(); 127 List<VirtualMachineDescriptor> vmds = VirtualMachine.list(); 128 for (VirtualMachineDescriptor vmd : vmds) { 129 if (check(vmd, excludeCls, partialMatch)) { 130 vids.add(vmd); 131 } 132 } 133 return vids; 134 } 135 136 public Collection<VirtualMachineDescriptor> getVirtualMachineDescriptors(Class<?> excludeClass) { 137 if (singlePid != null) { 138 return getSingleVMD(singlePid); 139 } else { 140 return getVMDs(excludeClass, matchClass); 141 } 142 } 143 144 public Collection<VirtualMachineDescriptor> getVirtualMachineDescriptors() { 145 return this.getVirtualMachineDescriptors(null); 146 } 147 148 public Collection<String> getVirtualMachinePids(Class<?> excludeClass) { 149 if (singlePid != null) { 150 // There is a bug in AttachProvider, when VM is debuggee-suspended it's not listed by the AttachProvider. 151 // If we are talking about a specific pid, just return it. 152 return List.of(singlePid); 153 } else { 154 return getVMDs(excludeClass, matchClass).stream().map(x -> {return x.id();}).collect(Collectors.toList()); 155 } 156 } 157 158 public Collection<String> getVirtualMachinePids() { 159 return this.getVirtualMachinePids(null); 160 } 161} 162