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