1 /*
   2  * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *com.sun.tools.attach.AttachNotSupportedException
   5 
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.  Oracle designates this
   9  * particular file as subject to the "Classpath" exception as provided
  10  * by Oracle in the LICENSE file that accompanied this code.
  11  *
  12  * This code is distributed in the hope that it will be useful, but WITHOUT
  13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15  * version 2 for more details (a copy is included in the LICENSE file that
  16  * accompanied this code).
  17  *
  18  * You should have received a copy of the GNU General Public License version
  19  * 2 along with this work; if not, write to the Free Software Foundation,
  20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  21  *
  22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  23  * or visit www.oracle.com if you need additional information or have any
  24  * questions.
  25  */
  26 
  27 package sun.tools.jcmd;
  28 
  29 import java.io.InputStream;
  30 import java.io.IOException;
  31 import java.io.UnsupportedEncodingException;
  32 import java.util.List;
  33 import java.util.ArrayList;
  34 import java.util.Comparator;
  35 import java.net.URISyntaxException;
  36 
  37 import com.sun.tools.attach.VirtualMachine;
  38 import com.sun.tools.attach.VirtualMachineDescriptor;
  39 import com.sun.tools.attach.AgentLoadException;
  40 import com.sun.tools.attach.AttachNotSupportedException;
  41 import sun.tools.attach.HotSpotVirtualMachine;
  42 import sun.tools.jstat.JStatLogger;
  43 import sun.jvmstat.monitor.Monitor;
  44 import sun.jvmstat.monitor.MonitoredHost;
  45 import sun.jvmstat.monitor.MonitoredVm;
  46 import sun.jvmstat.monitor.MonitoredVmUtil;
  47 import sun.jvmstat.monitor.MonitorException;
  48 import sun.jvmstat.monitor.VmIdentifier;
  49 
  50 public class JCmd {
  51     public static void main(String[] args) {
  52         Arguments arg = null;
  53         try {
  54             arg = new Arguments(args);
  55         } catch (IllegalArgumentException ex) {
  56             System.err.println("Error parsing arguments: " + ex.getMessage()
  57                                + "\n");
  58             Arguments.usage();
  59             System.exit(1);
  60         }
  61 
  62         if (arg.isShowUsage()) {
  63             Arguments.usage();
  64             System.exit(1);
  65         }
  66 
  67         if (arg.isListProcesses()) {
  68             List<VirtualMachineDescriptor> vmds = VirtualMachine.list();
  69             for (VirtualMachineDescriptor vmd : vmds) {
  70                 System.out.println(vmd.id() + " " + vmd.displayName());
  71             }
  72             System.exit(0);
  73         }
  74 
  75         List<String> pids = new ArrayList<String>();
  76         if (arg.getPid() == 0) {
  77             // find all VMs
  78             List<VirtualMachineDescriptor> vmds = VirtualMachine.list();
  79             for (VirtualMachineDescriptor vmd : vmds) {
  80                 if (!isJCmdProcess(vmd)) {
  81                     pids.add(vmd.id());
  82                 }
  83             }
  84         } else if (arg.getProcessSubstring() != null) {
  85             // use the partial class-name match
  86             List<VirtualMachineDescriptor> vmds = VirtualMachine.list();
  87             for (VirtualMachineDescriptor vmd : vmds) {
  88                 if (isJCmdProcess(vmd)) {
  89                     continue;
  90                 }
  91                 try {
  92                     String mainClass = getMainClass(vmd);
  93                     if (mainClass != null
  94                         && mainClass.indexOf(arg.getProcessSubstring()) != -1) {
  95                             pids.add(vmd.id());
  96                     }
  97                 } catch (MonitorException|URISyntaxException e) {
  98                     if (e.getMessage() != null) {
  99                         System.err.println(e.getMessage());
 100                     } else {
 101                         Throwable cause = e.getCause();
 102                         if ((cause != null) && (cause.getMessage() != null)) {
 103                             System.err.println(cause.getMessage());
 104                         } else {
 105                             e.printStackTrace();
 106                         }
 107                     }
 108                 }
 109             }
 110             if (pids.isEmpty()) {
 111                 System.err.println("Could not find any processes matching : '"
 112                                    + arg.getProcessSubstring() + "'");
 113                 System.exit(1);
 114             }
 115         } else if (arg.getPid() == -1) {
 116             System.err.println("Invalid pid specified");
 117             System.exit(1);
 118         } else {
 119             // Use the found pid
 120             pids.add(arg.getPid() + "");
 121         }
 122 
 123         for (String pid : pids) {
 124             System.out.println(pid + ":");
 125             if (arg.isListCounters()) {
 126                 listCounters(pid);
 127             } else {
 128                 try {
 129                     executeCommandForPid(pid, arg.getCommand());
 130                 } catch(Exception ex) {
 131                     ex.printStackTrace();
 132                 }
 133             }
 134         }
 135     }
 136 
 137     private static void executeCommandForPid(String pid, String command)
 138         throws AttachNotSupportedException, IOException,
 139                UnsupportedEncodingException {
 140         VirtualMachine vm = VirtualMachine.attach(pid);
 141 
 142         // Cast to HotSpotVirtualMachine as this is an
 143         // implementation specific method.
 144         HotSpotVirtualMachine hvm = (HotSpotVirtualMachine) vm;
 145         try (InputStream in = hvm.executeJCmd(command);) {
 146             // read to EOF and just print output
 147             byte b[] = new byte[256];
 148             int n;
 149             do {
 150                 n = in.read(b);
 151                 if (n > 0) {
 152                     String s = new String(b, 0, n, "UTF-8");
 153                     System.out.print(s);
 154                 }
 155             } while (n > 0);
 156         }
 157         vm.detach();
 158     }
 159 
 160     private static void listCounters(String pid) {
 161         // Code from JStat (can't call it directly since it does System.exit)
 162         VmIdentifier vmId = null;
 163         try {
 164             vmId = new VmIdentifier(pid);
 165         } catch (URISyntaxException e) {
 166             System.err.println("Malformed VM Identifier: " + pid);
 167             return;
 168         }
 169         try {
 170             MonitoredHost monitoredHost = MonitoredHost.getMonitoredHost(vmId);
 171             MonitoredVm monitoredVm = monitoredHost.getMonitoredVm(vmId, -1);
 172             JStatLogger logger = new JStatLogger(monitoredVm);
 173             logger.printSnapShot("\\w*", // all names
 174                     new AscendingMonitorComparator(), // comparator
 175                     false, // not verbose
 176                     true, // show unsupported
 177                     System.out);
 178             monitoredHost.detach(monitoredVm);
 179         } catch (MonitorException ex) {
 180             ex.printStackTrace();
 181         }
 182     }
 183 
 184     private static boolean isJCmdProcess(VirtualMachineDescriptor vmd) {
 185         try {
 186             String mainClass = getMainClass(vmd);
 187             return mainClass != null && mainClass.equals(JCmd.class.getName());
 188         } catch (URISyntaxException|MonitorException ex) {
 189             return false;
 190         }
 191     }
 192 
 193     private static String getMainClass(VirtualMachineDescriptor vmd)
 194             throws URISyntaxException, MonitorException {
 195         try {
 196             String mainClass = null;
 197             VmIdentifier vmId = new VmIdentifier(vmd.id());
 198             MonitoredHost monitoredHost = MonitoredHost.getMonitoredHost(vmId);
 199             MonitoredVm monitoredVm = monitoredHost.getMonitoredVm(vmId, -1);
 200             mainClass = MonitoredVmUtil.mainClass(monitoredVm, true);
 201             monitoredHost.detach(monitoredVm);
 202             return mainClass;
 203         } catch(NullPointerException e) {
 204             // There is a potential race, where a running java app is being
 205             // queried, unfortunately the java app has shutdown after this
 206             // method is started but before getMonitoredVM is called.
 207             // If this is the case, then the /tmp/hsperfdata_xxx/pid file
 208             // will have disappeared and we will get a NullPointerException.
 209             // Handle this gracefully....
 210             return null;
 211         }
 212     }
 213 
 214     /**
 215      * Class to compare two Monitor objects by name in ascending order.
 216      * (from jstat)
 217      */
 218     static class AscendingMonitorComparator implements Comparator<Monitor> {
 219 
 220         public int compare(Monitor m1, Monitor m2) {
 221             String name1 = m1.getName();
 222             String name2 = m2.getName();
 223             return name1.compareTo(name2);
 224         }
 225     }
 226 }