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         return commonHelp(mode, false);
  50     }
  51 
  52     private static boolean commonHelpWithRemote(String mode) {
  53         return commonHelp(mode, true);
  54     }
  55 
  56     private static boolean commonHelp(String mode, boolean canConnectToRemote) {
  57         // --pid <pid>
  58         // --exe <exe>
  59         // --core <core>
  60         // --connect <[id@]server>
  61         System.out.println("    --pid <pid>           \tTo attach to and operate on the given");
  62         System.out.println("                          \tlive process.");
  63         System.out.println("    --core <corefile>     \tTo operate on the given core file.");
  64         System.out.println("    --exe <executable for corefile>");
  65         if (canConnectToRemote) {
  66             System.out.println("    --connect <[id@]server>\tTo operate on the remote debug server.");
  67         }
  68         System.out.println();
  69         System.out.println("    The --core and --exe options must be set together to give the core");
  70         System.out.println("    file, and associated executable, to operate on. They can use");
  71         System.out.println("    absolute or relative paths.");
  72         System.out.println("    The --pid option can be set to operate on a live process.");
  73         if (canConnectToRemote) {
  74             System.out.println("    The --connect option can be set to operate on a debug server.");
  75             System.out.println("    <--core --exe> and <--pid> and <--connect> are exclusive.");
  76         } else {
  77             System.out.println("    <--core --exe> and <--pid> are exclusive.");
  78         }
  79         System.out.println();
  80         System.out.println("    Examples: jhsdb " + mode + " --pid 1234");
  81         System.out.println("          or  jhsdb " + mode + " --core ./core.1234 --exe ./myexe");
  82         if (canConnectToRemote) {
  83             System.out.println("          or  jhsdb " + mode + " --connect debugserver");
  84             System.out.println("          or  jhsdb " + mode + " --connect id@debugserver");
  85         }
  86         return false;
  87     }
  88 
  89     private static boolean debugdHelp() {
  90         // [options] <pid> [server-id]
  91         // [options] <executable> <core> [server-id]
  92         System.out.println("    --serverid <id>       \tA unique identifier for this debug server.");
  93         return commonHelp("debugd");
  94     }
  95 
  96     private static boolean jinfoHelp() {
  97         // --flags -> -flags
  98         // --sysprops -> -sysprops
  99         System.out.println("    --flags               \tTo print VM flags.");
 100         System.out.println("    --sysprops            \tTo print Java System properties.");
 101         System.out.println("    <no option>           \tTo print both of the above.");
 102         return commonHelpWithRemote("jinfo");
 103     }
 104 
 105     private static boolean jmapHelp() {
 106         // --heap -> -heap
 107         // --binaryheap -> -heap:format=b
 108         // --histo -> -histo
 109         // --clstats -> -clstats
 110         // --finalizerinfo -> -finalizerinfo
 111 
 112         System.out.println("    <no option>           \tTo print same info as Solaris pmap.");
 113         System.out.println("    --heap                \tTo print java heap summary.");
 114         System.out.println("    --binaryheap          \tTo dump java heap in hprof binary format.");
 115         System.out.println("    --dumpfile <name>     \tThe name of the dump file.");
 116         System.out.println("    --histo               \tTo print histogram of java object heap.");
 117         System.out.println("    --clstats             \tTo print class loader statistics.");
 118         System.out.println("    --finalizerinfo       \tTo print information on objects awaiting finalization.");
 119         return commonHelpWithRemote("jmap");
 120     }
 121 
 122     private static boolean jstackHelp() {
 123         // --locks -> -l
 124         // --mixed -> -m
 125         System.out.println("    --locks               \tTo print java.util.concurrent locks.");
 126         System.out.println("    --mixed               \tTo print both Java and native frames (mixed mode).");
 127         return commonHelpWithRemote("jstack");
 128     }
 129 
 130     private static boolean jsnapHelp() {
 131         System.out.println("    --all                 \tTo print all performance counters.");
 132         return commonHelpWithRemote("jsnap");
 133     }
 134 
 135     private static boolean toolHelp(String toolName) {
 136         if (toolName.equals("jstack")) {
 137             return jstackHelp();
 138         }
 139         if (toolName.equals("jinfo")) {
 140             return jinfoHelp();
 141         }
 142         if (toolName.equals("jmap")) {
 143             return jmapHelp();
 144         }
 145         if (toolName.equals("jsnap")) {
 146             return jsnapHelp();
 147         }
 148         if (toolName.equals("debugd")) {
 149             return debugdHelp();
 150         }
 151         if (toolName.equals("hsdb")) {
 152             return commonHelp("hsdb");
 153         }
 154         if (toolName.equals("clhsdb")) {
 155             return commonHelp("clhsdb");
 156         }
 157         return launcherHelp();
 158     }
 159 
 160     private static final String NO_REMOTE = null;
 161 
 162     private static void buildAttachArgs(ArrayList<String> newArgs, String pid,
 163                                   String exe, String core, String remote, boolean allowEmpty) {
 164         if (!allowEmpty && (pid == null) && (exe == null) && (remote == null)) {
 165             throw new SAGetoptException("You have to set --pid or --exe or --connect.");
 166         }
 167 
 168         if (pid != null) { // Attach to live process
 169             if (exe != null) {
 170                 throw new SAGetoptException("Unnecessary argument: --exe");
 171             } else if (core != null) {
 172                 throw new SAGetoptException("Unnecessary argument: --core");
 173             } else if (remote != null) {
 174                 throw new SAGetoptException("Unnecessary argument: --connect");
 175             } else if (!pid.matches("^\\d+$")) {
 176                 throw new SAGetoptException("Invalid pid: " + pid);
 177             }
 178 
 179             newArgs.add(pid);
 180         } else if (exe != null) {
 181             if (remote != null) {
 182                 throw new SAGetoptException("Unnecessary argument: --connect");
 183             } else if (exe.length() == 0) {
 184                 throw new SAGetoptException("You have to set --exe.");
 185             }
 186 
 187             newArgs.add(exe);
 188 
 189             if ((core == null) || (core.length() == 0)) {
 190                 throw new SAGetoptException("You have to set --core.");
 191             }
 192 
 193             newArgs.add(core);
 194         } else if (remote != null) {
 195             newArgs.add(remote);
 196         }
 197     }
 198 
 199     private static void runCLHSDB(String[] oldArgs) {
 200         SAGetopt sg = new SAGetopt(oldArgs);
 201         String[] longOpts = {"exe=", "core=", "pid="};
 202 
 203         ArrayList<String> newArgs = new ArrayList();
 204         String pid = null;
 205         String exe = null;
 206         String core = null;
 207         String s = null;
 208 
 209         while((s = sg.next(null, longOpts)) != null) {
 210             if (s.equals("exe")) {
 211                 exe = sg.getOptarg();
 212                 continue;
 213             }
 214             if (s.equals("core")) {
 215                 core = sg.getOptarg();
 216                 continue;
 217             }
 218             if (s.equals("pid")) {
 219                 pid = sg.getOptarg();
 220                 continue;
 221             }
 222         }
 223 
 224         buildAttachArgs(newArgs, pid, exe, core, NO_REMOTE, true);
 225         CLHSDB.main(newArgs.toArray(new String[newArgs.size()]));
 226     }
 227 
 228     private static void runHSDB(String[] oldArgs) {
 229         SAGetopt sg = new SAGetopt(oldArgs);
 230         String[] longOpts = {"exe=", "core=", "pid="};
 231 
 232         ArrayList<String> newArgs = new ArrayList();
 233         String pid = null;
 234         String exe = null;
 235         String core = null;
 236         String s = null;
 237 
 238         while((s = sg.next(null, longOpts)) != null) {
 239             if (s.equals("exe")) {
 240                 exe = sg.getOptarg();
 241                 continue;
 242             }
 243             if (s.equals("core")) {
 244                 core = sg.getOptarg();
 245                 continue;
 246             }
 247             if (s.equals("pid")) {
 248                 pid = sg.getOptarg();
 249                 continue;
 250             }
 251         }
 252 
 253         buildAttachArgs(newArgs, pid, exe, core, NO_REMOTE, true);
 254         HSDB.main(newArgs.toArray(new String[newArgs.size()]));
 255     }
 256 
 257     private static void runJSTACK(String[] oldArgs) {
 258         SAGetopt sg = new SAGetopt(oldArgs);
 259         String[] longOpts = {"exe=", "core=", "pid=", "connect=",
 260                                  "mixed", "locks"};
 261 
 262         ArrayList<String> newArgs = new ArrayList();
 263         String pid = null;
 264         String exe = null;
 265         String core = null;
 266         String remote = NO_REMOTE;
 267         String s = null;
 268 
 269         while((s = sg.next(null, longOpts)) != null) {
 270             if (s.equals("exe")) {
 271                 exe = sg.getOptarg();
 272                 continue;
 273             }
 274             if (s.equals("core")) {
 275                 core = sg.getOptarg();
 276                 continue;
 277             }
 278             if (s.equals("pid")) {
 279                 pid = sg.getOptarg();
 280                 continue;
 281             }
 282             if (s.equals("connect")) {
 283                 remote = sg.getOptarg();
 284                 continue;
 285             }
 286             if (s.equals("mixed")) {
 287                 newArgs.add("-m");
 288                 continue;
 289             }
 290             if (s.equals("locks")) {
 291                 newArgs.add("-l");
 292                 continue;
 293             }
 294         }
 295 
 296         buildAttachArgs(newArgs, pid, exe, core, remote, false);
 297         JStack jstack = new JStack(false, false);
 298         jstack.runWithArgs(newArgs.toArray(new String[newArgs.size()]));
 299     }
 300 
 301     private static void runJMAP(String[] oldArgs) {
 302         SAGetopt sg = new SAGetopt(oldArgs);
 303         String[] longOpts = {"exe=", "core=", "pid=", "connect=",
 304               "heap", "binaryheap", "dumpfile=", "histo", "clstats", "finalizerinfo"};
 305 
 306         ArrayList<String> newArgs = new ArrayList();
 307         String pid = null;
 308         String exe = null;
 309         String core = null;
 310         String remote = NO_REMOTE;
 311         String s = null;
 312         String dumpfile = null;
 313         boolean requestHeapdump = false;
 314 
 315         while((s = sg.next(null, longOpts)) != null) {
 316             if (s.equals("exe")) {
 317                 exe = sg.getOptarg();
 318                 continue;
 319             }
 320             if (s.equals("core")) {
 321                 core = sg.getOptarg();
 322                 continue;
 323             }
 324             if (s.equals("pid")) {
 325                 pid = sg.getOptarg();
 326                 continue;
 327             }
 328             if (s.equals("connect")) {
 329                 remote = sg.getOptarg();
 330                 continue;
 331             }
 332             if (s.equals("heap")) {
 333                 newArgs.add("-heap");
 334                 continue;
 335             }
 336             if (s.equals("binaryheap")) {
 337                 requestHeapdump = true;
 338                 continue;
 339             }
 340             if (s.equals("dumpfile")) {
 341                 dumpfile = sg.getOptarg();
 342                 continue;
 343             }
 344             if (s.equals("histo")) {
 345                 newArgs.add("-histo");
 346                 continue;
 347             }
 348             if (s.equals("clstats")) {
 349                 newArgs.add("-clstats");
 350                 continue;
 351             }
 352             if (s.equals("finalizerinfo")) {
 353                 newArgs.add("-finalizerinfo");
 354                 continue;
 355             }
 356         }
 357 
 358         if (!requestHeapdump && (dumpfile != null)) {
 359             throw new IllegalArgumentException("Unexpected argument dumpfile");
 360         }
 361         if (requestHeapdump) {
 362             if (dumpfile == null) {
 363                 newArgs.add("-heap:format=b");
 364             } else {
 365                 newArgs.add("-heap:format=b,file=" + dumpfile);
 366             }
 367         }
 368 
 369         buildAttachArgs(newArgs, pid, exe, core, remote, false);
 370         JMap.main(newArgs.toArray(new String[newArgs.size()]));
 371     }
 372 
 373     private static void runJINFO(String[] oldArgs) {
 374         SAGetopt sg = new SAGetopt(oldArgs);
 375         String[] longOpts = {"exe=", "core=", "pid=", "connect=",
 376                                      "flags", "sysprops"};
 377 
 378         ArrayList<String> newArgs = new ArrayList();
 379         String exe = null;
 380         String pid = null;
 381         String core = null;
 382         String remote = NO_REMOTE;
 383         String s = null;
 384 
 385         while((s = sg.next(null, longOpts)) != null) {
 386             if (s.equals("exe")) {
 387                 exe = sg.getOptarg();
 388                 continue;
 389             }
 390             if (s.equals("core")) {
 391                 core = sg.getOptarg();
 392                 continue;
 393             }
 394             if (s.equals("pid")) {
 395                 pid = sg.getOptarg();
 396                 continue;
 397             }
 398             if (s.equals("connect")) {
 399                 remote = sg.getOptarg();
 400                 continue;
 401             }
 402             if (s.equals("flags")) {
 403                 newArgs.add("-flags");
 404                 continue;
 405             }
 406             if (s.equals("sysprops")) {
 407                 newArgs.add("-sysprops");
 408                 continue;
 409             }
 410         }
 411 
 412         buildAttachArgs(newArgs, pid, exe, core, remote, false);
 413         JInfo.main(newArgs.toArray(new String[newArgs.size()]));
 414     }
 415 
 416     private static void runJSNAP(String[] oldArgs) {
 417         SAGetopt sg = new SAGetopt(oldArgs);
 418         String[] longOpts = {"exe=", "core=", "pid=", "connect=", "all"};
 419 
 420         ArrayList<String> newArgs = new ArrayList();
 421         String exe = null;
 422         String pid = null;
 423         String core = null;
 424         String remote = NO_REMOTE;
 425         String s = null;
 426 
 427         while((s = sg.next(null, longOpts)) != null) {
 428             if (s.equals("exe")) {
 429                 exe = sg.getOptarg();
 430                 continue;
 431             }
 432             if (s.equals("core")) {
 433                 core = sg.getOptarg();
 434                 continue;
 435             }
 436             if (s.equals("pid")) {
 437                 pid = sg.getOptarg();
 438                 continue;
 439             }
 440             if (s.equals("connect")) {
 441                 remote = sg.getOptarg();
 442                 continue;
 443             }
 444             if (s.equals("all")) {
 445                 newArgs.add("-a");
 446                 continue;
 447             }
 448         }
 449 
 450         buildAttachArgs(newArgs, pid, exe, core, remote, false);
 451         JSnap.main(newArgs.toArray(new String[newArgs.size()]));
 452     }
 453 
 454     private static void runDEBUGD(String[] oldArgs) {
 455         // By default SA agent classes prefer Windows process debugger
 456         // to windbg debugger. SA expects special properties to be set
 457         // to choose other debuggers. We will set those here before
 458         // attaching to SA agent.
 459         System.setProperty("sun.jvm.hotspot.debugger.useWindbgDebugger", "true");
 460 
 461         SAGetopt sg = new SAGetopt(oldArgs);
 462         String[] longOpts = {"exe=", "core=", "pid=", "serverid="};
 463 
 464         ArrayList<String> newArgs = new ArrayList<>();
 465         String exe = null;
 466         String pid = null;
 467         String core = null;
 468         String s = null;
 469         String serverid = null;
 470 
 471         while((s = sg.next(null, longOpts)) != null) {
 472           if (s.equals("exe")) {
 473               exe = sg.getOptarg();
 474               continue;
 475           }
 476           if (s.equals("core")) {
 477               core = sg.getOptarg();
 478               continue;
 479           }
 480           if (s.equals("pid")) {
 481               pid = sg.getOptarg();
 482               continue;
 483           }
 484           if (s.equals("serverid")) {
 485               serverid = sg.getOptarg();
 486               continue;
 487           }
 488         }
 489 
 490         buildAttachArgs(newArgs, pid, exe, core, NO_REMOTE, false);
 491         if (serverid != null) {
 492             newArgs.add(serverid);
 493         }
 494 
 495         // delegate to the actual SA debug server.
 496         sun.jvm.hotspot.DebugServer.main(newArgs.toArray(new String[newArgs.size()]));
 497     }
 498 
 499     public static void main(String[] args) {
 500         // Provide a help
 501         if (args.length == 0) {
 502             launcherHelp();
 503             return;
 504         }
 505         // No arguments imply help for debugd, jstack, jmap, jinfo but launch clhsdb and hsdb
 506         if (args.length == 1 && !args[0].equals("clhsdb") && !args[0].equals("hsdb")) {
 507             toolHelp(args[0]);
 508             return;
 509         }
 510 
 511         for (String arg : args) {
 512             if (arg.equals("-h") || arg.equals("-help") || arg.equals("--help")) {
 513                 toolHelp(args[0]);
 514                 return;
 515             }
 516         }
 517 
 518         String[] oldArgs = Arrays.copyOfRange(args, 1, args.length);
 519 
 520         try {
 521             // Run SA interactive mode
 522             if (args[0].equals("clhsdb")) {
 523                 runCLHSDB(oldArgs);
 524                 return;
 525             }
 526 
 527             if (args[0].equals("hsdb")) {
 528                 runHSDB(oldArgs);
 529                 return;
 530             }
 531 
 532             // Run SA tmtools mode
 533             if (args[0].equals("jstack")) {
 534                 runJSTACK(oldArgs);
 535                 return;
 536             }
 537 
 538             if (args[0].equals("jmap")) {
 539                 runJMAP(oldArgs);
 540                 return;
 541             }
 542 
 543             if (args[0].equals("jinfo")) {
 544                 runJINFO(oldArgs);
 545                 return;
 546             }
 547 
 548             if (args[0].equals("jsnap")) {
 549                 runJSNAP(oldArgs);
 550                 return;
 551             }
 552 
 553             if (args[0].equals("debugd")) {
 554                 runDEBUGD(oldArgs);
 555                 return;
 556             }
 557 
 558             throw new SAGetoptException("Unknown tool: " + args[0]);
 559         } catch (SAGetoptException e) {
 560             System.err.println(e.getMessage());
 561             toolHelp(args[0]);
 562         }
 563     }
 564 }