1/*
2 * Copyright (c) 2015, 2016, 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
25package sun.jvm.hotspot;
26
27import java.util.ArrayList;
28import java.util.Arrays;
29
30import sun.jvm.hotspot.tools.JStack;
31import sun.jvm.hotspot.tools.JMap;
32import sun.jvm.hotspot.tools.JInfo;
33import sun.jvm.hotspot.tools.JSnap;
34
35public class SALauncher {
36
37    private static boolean launcherHelp() {
38        System.out.println("    clhsdb       \tcommand line debugger");
39        System.out.println("    debugd       \tdebug server");
40        System.out.println("    hsdb         \tui debugger");
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() {
49        // --pid <pid>
50        // --exe <exe>
51        // --core <core>
52        System.out.println("    --exe\texecutable image name");
53        System.out.println("    --core\tpath to coredump");
54        System.out.println("    --pid\tpid of process to attach");
55        return false;
56    }
57
58    private static boolean debugdHelp() {
59        // [options] <pid> [server-id]
60        // [options] <executable> <core> [server-id]
61        java.io.PrintStream out = System.out;
62        out.print(" [option] <pid> [server-id]");
63        out.println("\t\t(to connect to a live java process)");
64        out.print("   or  [option] <executable> <core> [server-id]");
65        out.println("\t\t(to connect to a core file produced by <executable>)");
66        out.print("\t\tserver-id is an optional unique id for this debug server, needed ");
67        out.println("\t\tif multiple debug servers are run on the same machine");
68        out.println("where option includes:");
69        out.println("   -h | -help\tto print this help message");
70        return false;
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();
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\tname 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();
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();
105    }
106
107    private static boolean jsnapHelp() {
108        System.out.println("    --all\tto print all performance counters");
109        return commonHelp();
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") || toolName.equals("clhsdb")) {
129            return commonHelp();
130        }
131        return launcherHelp();
132    }
133
134    private static void buildAttachArgs(ArrayList<String> newArgs, String pid,
135                                  String exe, String core, boolean allowEmpty) {
136        if (!allowEmpty && (pid == null) && (exe == null)) {
137            throw new SAGetoptException("You have to set --pid or --exe.");
138        }
139
140        if (pid != null) { // Attach to live process
141            if (exe != null) {
142                throw new SAGetoptException("Unnecessary argument: --exe");
143            } else if (core != null) {
144                throw new SAGetoptException("Unnecessary argument: --core");
145            } else if (!pid.matches("^\\d+$")) {
146                throw new SAGetoptException("Invalid pid: " + pid);
147            }
148
149            newArgs.add(pid);
150        } else if (exe != null) {
151            if (exe.length() == 0) {
152                throw new SAGetoptException("You have to set --exe.");
153            }
154
155            newArgs.add(exe);
156
157            if ((core == null) || (core.length() == 0)) {
158                throw new SAGetoptException("You have to set --core.");
159            }
160
161            newArgs.add(core);
162        }
163    }
164
165    private static void runCLHSDB(String[] oldArgs) {
166        SAGetopt sg = new SAGetopt(oldArgs);
167        String[] longOpts = {"exe=", "core=", "pid="};
168
169        ArrayList<String> newArgs = new ArrayList();
170        String pid = null;
171        String exe = null;
172        String core = null;
173        String s = null;
174
175        while((s = sg.next(null, longOpts)) != null) {
176            if (s.equals("exe")) {
177                exe = sg.getOptarg();
178                continue;
179            }
180            if (s.equals("core")) {
181                core = sg.getOptarg();
182                continue;
183            }
184            if (s.equals("pid")) {
185                pid = sg.getOptarg();
186                continue;
187            }
188        }
189
190        buildAttachArgs(newArgs, pid, exe, core, true);
191        CLHSDB.main(newArgs.toArray(new String[newArgs.size()]));
192    }
193
194    private static void runHSDB(String[] oldArgs) {
195        SAGetopt sg = new SAGetopt(oldArgs);
196        String[] longOpts = {"exe=", "core=", "pid="};
197
198        ArrayList<String> newArgs = new ArrayList();
199        String pid = null;
200        String exe = null;
201        String core = null;
202        String s = null;
203
204        while((s = sg.next(null, longOpts)) != null) {
205            if (s.equals("exe")) {
206                exe = sg.getOptarg();
207                continue;
208            }
209            if (s.equals("core")) {
210                core = sg.getOptarg();
211                continue;
212            }
213            if (s.equals("pid")) {
214                pid = sg.getOptarg();
215                continue;
216            }
217        }
218
219        buildAttachArgs(newArgs, pid, exe, core, true);
220        HSDB.main(newArgs.toArray(new String[newArgs.size()]));
221    }
222
223    private static void runJSTACK(String[] oldArgs) {
224        SAGetopt sg = new SAGetopt(oldArgs);
225        String[] longOpts = {"exe=", "core=", "pid=",
226                                 "mixed", "locks"};
227
228        ArrayList<String> newArgs = new ArrayList();
229        String pid = null;
230        String exe = null;
231        String core = null;
232        String s = null;
233
234        while((s = sg.next(null, longOpts)) != null) {
235            if (s.equals("exe")) {
236                exe = sg.getOptarg();
237                continue;
238            }
239            if (s.equals("core")) {
240                core = sg.getOptarg();
241                continue;
242            }
243            if (s.equals("pid")) {
244                pid = sg.getOptarg();
245                continue;
246            }
247            if (s.equals("mixed")) {
248                newArgs.add("-m");
249                continue;
250            }
251            if (s.equals("locks")) {
252                newArgs.add("-l");
253                continue;
254            }
255        }
256
257        buildAttachArgs(newArgs, pid, exe, core, false);
258        JStack jstack = new JStack(false, false);
259        jstack.runWithArgs(newArgs.toArray(new String[newArgs.size()]));
260    }
261
262    private static void runJMAP(String[] oldArgs) {
263        SAGetopt sg = new SAGetopt(oldArgs);
264        String[] longOpts = {"exe=", "core=", "pid=",
265              "heap", "binaryheap", "dumpfile=", "histo", "clstats", "finalizerinfo"};
266
267        ArrayList<String> newArgs = new ArrayList();
268        String pid = null;
269        String exe = null;
270        String core = null;
271        String s = null;
272        String dumpfile = null;
273        boolean requestHeapdump = false;
274
275        while((s = sg.next(null, longOpts)) != null) {
276            if (s.equals("exe")) {
277                exe = sg.getOptarg();
278                continue;
279            }
280            if (s.equals("core")) {
281                core = sg.getOptarg();
282                continue;
283            }
284            if (s.equals("pid")) {
285                pid = sg.getOptarg();
286                continue;
287            }
288            if (s.equals("heap")) {
289                newArgs.add("-heap");
290                continue;
291            }
292            if (s.equals("binaryheap")) {
293                requestHeapdump = true;
294                continue;
295            }
296            if (s.equals("dumpfile")) {
297                dumpfile = sg.getOptarg();
298                continue;
299            }
300            if (s.equals("histo")) {
301                newArgs.add("-histo");
302                continue;
303            }
304            if (s.equals("clstats")) {
305                newArgs.add("-clstats");
306                continue;
307            }
308            if (s.equals("finalizerinfo")) {
309                newArgs.add("-finalizerinfo");
310                continue;
311            }
312        }
313
314        if (!requestHeapdump && (dumpfile != null)) {
315            throw new IllegalArgumentException("Unexpected argument dumpfile");
316        }
317        if (requestHeapdump) {
318            if (dumpfile == null) {
319                newArgs.add("-heap:format=b");
320            } else {
321                newArgs.add("-heap:format=b,file=" + dumpfile);
322            }
323        }
324
325        buildAttachArgs(newArgs, pid, exe, core, false);
326        JMap.main(newArgs.toArray(new String[newArgs.size()]));
327    }
328
329    private static void runJINFO(String[] oldArgs) {
330        SAGetopt sg = new SAGetopt(oldArgs);
331        String[] longOpts = {"exe=", "core=", "pid=",
332                                     "flags", "sysprops"};
333
334        ArrayList<String> newArgs = new ArrayList();
335        String exe = null;
336        String pid = null;
337        String core = null;
338        String s = null;
339
340        while((s = sg.next(null, longOpts)) != null) {
341            if (s.equals("exe")) {
342                exe = sg.getOptarg();
343                continue;
344            }
345            if (s.equals("core")) {
346                core = sg.getOptarg();
347                continue;
348            }
349            if (s.equals("pid")) {
350                pid = sg.getOptarg();
351                continue;
352            }
353            if (s.equals("flags")) {
354                newArgs.add("-flags");
355                continue;
356            }
357            if (s.equals("sysprops")) {
358                newArgs.add("-sysprops");
359                continue;
360            }
361        }
362
363        buildAttachArgs(newArgs, pid, exe, core, false);
364        JInfo.main(newArgs.toArray(new String[newArgs.size()]));
365    }
366
367    private static void runJSNAP(String[] oldArgs) {
368        SAGetopt sg = new SAGetopt(oldArgs);
369        String[] longOpts = {"exe=", "core=", "pid=", "all"};
370
371        ArrayList<String> newArgs = new ArrayList();
372        String exe = null;
373        String pid = null;
374        String core = null;
375        String s = null;
376
377        while((s = sg.next(null, longOpts)) != null) {
378            if (s.equals("exe")) {
379                exe = sg.getOptarg();
380                continue;
381            }
382            if (s.equals("core")) {
383                core = sg.getOptarg();
384                continue;
385            }
386            if (s.equals("pid")) {
387                pid = sg.getOptarg();
388                continue;
389            }
390            if (s.equals("all")) {
391                newArgs.add("-a");
392                continue;
393            }
394        }
395
396        buildAttachArgs(newArgs, pid, exe, core, false);
397        JSnap.main(newArgs.toArray(new String[newArgs.size()]));
398    }
399
400    private static void runDEBUGD(String[] oldArgs) {
401        if ((oldArgs.length < 1) || (oldArgs.length > 3)) {
402            debugdHelp();
403        }
404
405        // By default SA agent classes prefer Windows process debugger
406        // to windbg debugger. SA expects special properties to be set
407        // to choose other debuggers. We will set those here before
408        // attaching to SA agent.
409        System.setProperty("sun.jvm.hotspot.debugger.useWindbgDebugger", "true");
410
411        // delegate to the actual SA debug server.
412        sun.jvm.hotspot.DebugServer.main(oldArgs);
413    }
414
415    public static void main(String[] args) {
416        // Provide a help
417        if (args.length == 0) {
418            launcherHelp();
419            return;
420        }
421        // No arguments imply help for debugd, jstack, jmap, jinfo but launch clhsdb and hsdb
422        if (args.length == 1 && !args[0].equals("clhsdb") && !args[0].equals("hsdb")) {
423            toolHelp(args[0]);
424            return;
425        }
426
427        for (String arg : args) {
428            if (arg.equals("-h") || arg.equals("-help") || arg.equals("--help")) {
429                toolHelp(args[0]);
430                return;
431            }
432        }
433
434        String[] oldArgs = Arrays.copyOfRange(args, 1, args.length);
435
436        try {
437            // Run SA interactive mode
438            if (args[0].equals("clhsdb")) {
439                runCLHSDB(oldArgs);
440                return;
441            }
442
443            if (args[0].equals("hsdb")) {
444                runHSDB(oldArgs);
445                return;
446            }
447
448            // Run SA tmtools mode
449            if (args[0].equals("jstack")) {
450                runJSTACK(oldArgs);
451                return;
452            }
453
454            if (args[0].equals("jmap")) {
455                runJMAP(oldArgs);
456                return;
457            }
458
459            if (args[0].equals("jinfo")) {
460                runJINFO(oldArgs);
461                return;
462            }
463
464            if (args[0].equals("jsnap")) {
465                runJSNAP(oldArgs);
466                return;
467            }
468
469            if (args[0].equals("debugd")) {
470                runDEBUGD(oldArgs);
471                return;
472            }
473
474            throw new SAGetoptException("Unknown tool: " + args[0]);
475        } catch (SAGetoptException e) {
476            System.err.println(e.getMessage());
477            toolHelp(args[0]);
478        }
479    }
480}
481