1 /*
   2  * Copyright (c) 2015, 2019, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot;
  26 
  27 import java.util.ArrayList;
  28 import java.util.Arrays;
  29 
  30 import sun.jvm.hotspot.tools.JStack;
  31 import sun.jvm.hotspot.tools.JMap;
  32 import sun.jvm.hotspot.tools.JInfo;
  33 import sun.jvm.hotspot.tools.JSnap;
  34 
  35 public class SALauncher {
  36 
  37     private static boolean launcherHelp() {
  38         System.out.println("    clhsdb       \tcommand line debugger");
  39         System.out.println("    hsdb         \tui debugger");
  40         System.out.println("    debugd --help\tto get more information");
  41         System.out.println("    jstack --help\tto get more information");
  42         System.out.println("    jmap   --help\tto get more information");
  43         System.out.println("    jinfo  --help\tto get more information");
  44         System.out.println("    jsnap  --help\tto get more information");
  45         return false;
  46     }
  47 
  48     private static boolean commonHelp(String mode) {
  49         // --pid <pid>
  50         // --exe <exe>
  51         // --core <core>
  52         System.out.println("    --pid <pid>      \tTo attach to and operate on the given live process.");
  53         System.out.println("    --core <corefile>\tTo operate on a given core file.");
  54         System.out.println("    --exe <executable for corefile>");
  55         System.out.println();
  56         System.out.println("    The --core and --exe options must be set together to give the core");
  57         System.out.println("    file, and associated executable, to operate on. Otherwise the --pid");
  58         System.out.println("    option can be set to operate on a live process.");
  59         System.out.println("    The arguments for --exe and --core can use absolute or relative paths.");
  60         System.out.println();
  61         System.out.println("    Examples: jhsdb " + mode + " --pid 1234");
  62         System.out.println("          or  jhsdb " + mode + " --core ./core.1234 --exe ./myexe");
  63         return false;
  64     }
  65 
  66     private static boolean debugdHelp() {
  67         // [options] <pid> [server-id]
  68         // [options] <executable> <core> [server-id]
  69         System.out.println("    --serverid <id>  \tA unique identifier for this debug server.");
  70         return commonHelp("debugd");
  71     }
  72 
  73     private static boolean jinfoHelp() {
  74         // --flags -> -flags
  75         // --sysprops -> -sysprops
  76         System.out.println("    --flags          \tTo print VM flags.");
  77         System.out.println("    --sysprops       \tTo print Java System properties.");
  78         System.out.println("    <no option>      \tTo print both of the above.");
  79         return commonHelp("jinfo");
  80     }
  81 
  82     private static boolean jmapHelp() {
  83         // --heap -> -heap
  84         // --binaryheap -> -heap:format=b
  85         // --histo -> -histo
  86         // --clstats -> -clstats
  87         // --finalizerinfo -> -finalizerinfo
  88 
  89         System.out.println("    <no option>      \tTo print same info as Solaris pmap.");
  90         System.out.println("    --heap           \tTo print java heap summary.");
  91         System.out.println("    --binaryheap     \tTo dump java heap in hprof binary format.");
  92         System.out.println("    --dumpfile <name>\tA Name of the dump file.");
  93         System.out.println("    --histo          \tTo print histogram of java object heap.");
  94         System.out.println("    --clstats        \tTo print class loader statistics.");
  95         System.out.println("    --finalizerinfo  \tTo print information on objects awaiting finalization.");
  96         return commonHelp("jmap");
  97     }
  98 
  99     private static boolean jstackHelp() {
 100         // --locks -> -l
 101         // --mixed -> -m
 102         System.out.println("    --locks          \tTo print java.util.concurrent locks.");
 103         System.out.println("    --mixed          \tTo print both Java and native frames (mixed mode).");
 104         return commonHelp("jstack");
 105     }
 106 
 107     private static boolean jsnapHelp() {
 108         System.out.println("    --all            \tTo print all performance counters.");
 109         return commonHelp("jsnap");
 110     }
 111 
 112     private static boolean toolHelp(String toolName) {
 113         if (toolName.equals("jstack")) {
 114             return jstackHelp();
 115         }
 116         if (toolName.equals("jinfo")) {
 117             return jinfoHelp();
 118         }
 119         if (toolName.equals("jmap")) {
 120             return jmapHelp();
 121         }
 122         if (toolName.equals("jsnap")) {
 123             return jsnapHelp();
 124         }
 125         if (toolName.equals("debugd")) {
 126             return debugdHelp();
 127         }
 128         if (toolName.equals("hsdb")) {
 129             return commonHelp("hsdb");
 130         }
 131         if (toolName.equals("clhsdb")) {
 132             return commonHelp("clhsdb");
 133         }
 134         return launcherHelp();
 135     }
 136 
 137     private static void buildAttachArgs(ArrayList<String> newArgs, String pid,
 138                                   String exe, String core, boolean allowEmpty) {
 139         if (!allowEmpty && (pid == null) && (exe == null)) {
 140             throw new SAGetoptException("You have to set --pid or --exe.");
 141         }
 142 
 143         if (pid != null) { // Attach to live process
 144             if (exe != null) {
 145                 throw new SAGetoptException("Unnecessary argument: --exe");
 146             } else if (core != null) {
 147                 throw new SAGetoptException("Unnecessary argument: --core");
 148             } else if (!pid.matches("^\\d+$")) {
 149                 throw new SAGetoptException("Invalid pid: " + pid);
 150             }
 151 
 152             newArgs.add(pid);
 153         } else if (exe != null) {
 154             if (exe.length() == 0) {
 155                 throw new SAGetoptException("You have to set --exe.");
 156             }
 157 
 158             newArgs.add(exe);
 159 
 160             if ((core == null) || (core.length() == 0)) {
 161                 throw new SAGetoptException("You have to set --core.");
 162             }
 163 
 164             newArgs.add(core);
 165         }
 166     }
 167 
 168     private static void runCLHSDB(String[] oldArgs) {
 169         SAGetopt sg = new SAGetopt(oldArgs);
 170         String[] longOpts = {"exe=", "core=", "pid="};
 171 
 172         ArrayList<String> newArgs = new ArrayList();
 173         String pid = null;
 174         String exe = null;
 175         String core = null;
 176         String s = null;
 177 
 178         while((s = sg.next(null, longOpts)) != null) {
 179             if (s.equals("exe")) {
 180                 exe = sg.getOptarg();
 181                 continue;
 182             }
 183             if (s.equals("core")) {
 184                 core = sg.getOptarg();
 185                 continue;
 186             }
 187             if (s.equals("pid")) {
 188                 pid = sg.getOptarg();
 189                 continue;
 190             }
 191         }
 192 
 193         buildAttachArgs(newArgs, pid, exe, core, true);
 194         CLHSDB.main(newArgs.toArray(new String[newArgs.size()]));
 195     }
 196 
 197     private static void runHSDB(String[] oldArgs) {
 198         SAGetopt sg = new SAGetopt(oldArgs);
 199         String[] longOpts = {"exe=", "core=", "pid="};
 200 
 201         ArrayList<String> newArgs = new ArrayList();
 202         String pid = null;
 203         String exe = null;
 204         String core = null;
 205         String s = null;
 206 
 207         while((s = sg.next(null, longOpts)) != null) {
 208             if (s.equals("exe")) {
 209                 exe = sg.getOptarg();
 210                 continue;
 211             }
 212             if (s.equals("core")) {
 213                 core = sg.getOptarg();
 214                 continue;
 215             }
 216             if (s.equals("pid")) {
 217                 pid = sg.getOptarg();
 218                 continue;
 219             }
 220         }
 221 
 222         buildAttachArgs(newArgs, pid, exe, core, true);
 223         HSDB.main(newArgs.toArray(new String[newArgs.size()]));
 224     }
 225 
 226     private static void runJSTACK(String[] oldArgs) {
 227         SAGetopt sg = new SAGetopt(oldArgs);
 228         String[] longOpts = {"exe=", "core=", "pid=",
 229                                  "mixed", "locks"};
 230 
 231         ArrayList<String> newArgs = new ArrayList();
 232         String pid = null;
 233         String exe = null;
 234         String core = null;
 235         String s = null;
 236 
 237         while((s = sg.next(null, longOpts)) != null) {
 238             if (s.equals("exe")) {
 239                 exe = sg.getOptarg();
 240                 continue;
 241             }
 242             if (s.equals("core")) {
 243                 core = sg.getOptarg();
 244                 continue;
 245             }
 246             if (s.equals("pid")) {
 247                 pid = sg.getOptarg();
 248                 continue;
 249             }
 250             if (s.equals("mixed")) {
 251                 newArgs.add("-m");
 252                 continue;
 253             }
 254             if (s.equals("locks")) {
 255                 newArgs.add("-l");
 256                 continue;
 257             }
 258         }
 259 
 260         buildAttachArgs(newArgs, pid, exe, core, false);
 261         JStack jstack = new JStack(false, false);
 262         jstack.runWithArgs(newArgs.toArray(new String[newArgs.size()]));
 263     }
 264 
 265     private static void runJMAP(String[] oldArgs) {
 266         SAGetopt sg = new SAGetopt(oldArgs);
 267         String[] longOpts = {"exe=", "core=", "pid=",
 268               "heap", "binaryheap", "dumpfile=", "histo", "clstats", "finalizerinfo"};
 269 
 270         ArrayList<String> newArgs = new ArrayList();
 271         String pid = null;
 272         String exe = null;
 273         String core = null;
 274         String s = null;
 275         String dumpfile = null;
 276         boolean requestHeapdump = false;
 277 
 278         while((s = sg.next(null, longOpts)) != null) {
 279             if (s.equals("exe")) {
 280                 exe = sg.getOptarg();
 281                 continue;
 282             }
 283             if (s.equals("core")) {
 284                 core = sg.getOptarg();
 285                 continue;
 286             }
 287             if (s.equals("pid")) {
 288                 pid = sg.getOptarg();
 289                 continue;
 290             }
 291             if (s.equals("heap")) {
 292                 newArgs.add("-heap");
 293                 continue;
 294             }
 295             if (s.equals("binaryheap")) {
 296                 requestHeapdump = true;
 297                 continue;
 298             }
 299             if (s.equals("dumpfile")) {
 300                 dumpfile = sg.getOptarg();
 301                 continue;
 302             }
 303             if (s.equals("histo")) {
 304                 newArgs.add("-histo");
 305                 continue;
 306             }
 307             if (s.equals("clstats")) {
 308                 newArgs.add("-clstats");
 309                 continue;
 310             }
 311             if (s.equals("finalizerinfo")) {
 312                 newArgs.add("-finalizerinfo");
 313                 continue;
 314             }
 315         }
 316 
 317         if (!requestHeapdump && (dumpfile != null)) {
 318             throw new IllegalArgumentException("Unexpected argument dumpfile");
 319         }
 320         if (requestHeapdump) {
 321             if (dumpfile == null) {
 322                 newArgs.add("-heap:format=b");
 323             } else {
 324                 newArgs.add("-heap:format=b,file=" + dumpfile);
 325             }
 326         }
 327 
 328         buildAttachArgs(newArgs, pid, exe, core, false);
 329         JMap.main(newArgs.toArray(new String[newArgs.size()]));
 330     }
 331 
 332     private static void runJINFO(String[] oldArgs) {
 333         SAGetopt sg = new SAGetopt(oldArgs);
 334         String[] longOpts = {"exe=", "core=", "pid=",
 335                                      "flags", "sysprops"};
 336 
 337         ArrayList<String> newArgs = new ArrayList();
 338         String exe = null;
 339         String pid = null;
 340         String core = null;
 341         String s = null;
 342 
 343         while((s = sg.next(null, longOpts)) != null) {
 344             if (s.equals("exe")) {
 345                 exe = sg.getOptarg();
 346                 continue;
 347             }
 348             if (s.equals("core")) {
 349                 core = sg.getOptarg();
 350                 continue;
 351             }
 352             if (s.equals("pid")) {
 353                 pid = sg.getOptarg();
 354                 continue;
 355             }
 356             if (s.equals("flags")) {
 357                 newArgs.add("-flags");
 358                 continue;
 359             }
 360             if (s.equals("sysprops")) {
 361                 newArgs.add("-sysprops");
 362                 continue;
 363             }
 364         }
 365 
 366         buildAttachArgs(newArgs, pid, exe, core, false);
 367         JInfo.main(newArgs.toArray(new String[newArgs.size()]));
 368     }
 369 
 370     private static void runJSNAP(String[] oldArgs) {
 371         SAGetopt sg = new SAGetopt(oldArgs);
 372         String[] longOpts = {"exe=", "core=", "pid=", "all"};
 373 
 374         ArrayList<String> newArgs = new ArrayList();
 375         String exe = null;
 376         String pid = null;
 377         String core = null;
 378         String s = null;
 379 
 380         while((s = sg.next(null, longOpts)) != null) {
 381             if (s.equals("exe")) {
 382                 exe = sg.getOptarg();
 383                 continue;
 384             }
 385             if (s.equals("core")) {
 386                 core = sg.getOptarg();
 387                 continue;
 388             }
 389             if (s.equals("pid")) {
 390                 pid = sg.getOptarg();
 391                 continue;
 392             }
 393             if (s.equals("all")) {
 394                 newArgs.add("-a");
 395                 continue;
 396             }
 397         }
 398 
 399         buildAttachArgs(newArgs, pid, exe, core, false);
 400         JSnap.main(newArgs.toArray(new String[newArgs.size()]));
 401     }
 402 
 403     private static void runDEBUGD(String[] oldArgs) {
 404         // By default SA agent classes prefer Windows process debugger
 405         // to windbg debugger. SA expects special properties to be set
 406         // to choose other debuggers. We will set those here before
 407         // attaching to SA agent.
 408         System.setProperty("sun.jvm.hotspot.debugger.useWindbgDebugger", "true");
 409 
 410         SAGetopt sg = new SAGetopt(oldArgs);
 411         String[] longOpts = {"exe=", "core=", "pid=", "serverid="};
 412 
 413         ArrayList<String> newArgs = new ArrayList<>();
 414         String exe = null;
 415         String pid = null;
 416         String core = null;
 417         String s = null;
 418         String serverid = null;
 419 
 420         while((s = sg.next(null, longOpts)) != null) {
 421           if (s.equals("exe")) {
 422               exe = sg.getOptarg();
 423               continue;
 424           }
 425           if (s.equals("core")) {
 426               core = sg.getOptarg();
 427               continue;
 428           }
 429           if (s.equals("pid")) {
 430               pid = sg.getOptarg();
 431               continue;
 432           }
 433           if (s.equals("serverid")) {
 434               serverid = sg.getOptarg();
 435               continue;
 436           }
 437         }
 438 
 439         buildAttachArgs(newArgs, pid, exe, core, false);
 440         if (serverid != null) {
 441             newArgs.add(serverid);
 442         }
 443 
 444         // delegate to the actual SA debug server.
 445         sun.jvm.hotspot.DebugServer.main(newArgs.toArray(new String[newArgs.size()]));
 446     }
 447 
 448     public static void main(String[] args) {
 449         // Provide a help
 450         if (args.length == 0) {
 451             launcherHelp();
 452             return;
 453         }
 454         // No arguments imply help for debugd, jstack, jmap, jinfo but launch clhsdb and hsdb
 455         if (args.length == 1 && !args[0].equals("clhsdb") && !args[0].equals("hsdb")) {
 456             toolHelp(args[0]);
 457             return;
 458         }
 459 
 460         for (String arg : args) {
 461             if (arg.equals("-h") || arg.equals("-help") || arg.equals("--help")) {
 462                 toolHelp(args[0]);
 463                 return;
 464             }
 465         }
 466 
 467         String[] oldArgs = Arrays.copyOfRange(args, 1, args.length);
 468 
 469         try {
 470             // Run SA interactive mode
 471             if (args[0].equals("clhsdb")) {
 472                 runCLHSDB(oldArgs);
 473                 return;
 474             }
 475 
 476             if (args[0].equals("hsdb")) {
 477                 runHSDB(oldArgs);
 478                 return;
 479             }
 480 
 481             // Run SA tmtools mode
 482             if (args[0].equals("jstack")) {
 483                 runJSTACK(oldArgs);
 484                 return;
 485             }
 486 
 487             if (args[0].equals("jmap")) {
 488                 runJMAP(oldArgs);
 489                 return;
 490             }
 491 
 492             if (args[0].equals("jinfo")) {
 493                 runJINFO(oldArgs);
 494                 return;
 495             }
 496 
 497             if (args[0].equals("jsnap")) {
 498                 runJSNAP(oldArgs);
 499                 return;
 500             }
 501 
 502             if (args[0].equals("debugd")) {
 503                 runDEBUGD(oldArgs);
 504                 return;
 505             }
 506 
 507             throw new SAGetoptException("Unknown tool: " + args[0]);
 508         } catch (SAGetoptException e) {
 509             System.err.println(e.getMessage());
 510             toolHelp(args[0]);
 511         }
 512     }
 513 }