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