Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/SALauncher.java
41159 views
1
/*
2
* Copyright (c) 2015, 2021, 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
import java.util.ArrayList;
30
import java.util.HashMap;
31
import java.util.List;
32
import java.util.Map;
33
import java.util.function.Consumer;
34
35
import sun.jvm.hotspot.debugger.DebuggerException;
36
import sun.jvm.hotspot.tools.JStack;
37
import sun.jvm.hotspot.tools.JMap;
38
import sun.jvm.hotspot.tools.JInfo;
39
import sun.jvm.hotspot.tools.JSnap;
40
41
public class SALauncher {
42
43
private static boolean launcherHelp() {
44
System.out.println(" clhsdb \tcommand line debugger");
45
System.out.println(" hsdb \tui debugger");
46
System.out.println(" debugd --help\tto get more information");
47
System.out.println(" jstack --help\tto get more information");
48
System.out.println(" jmap --help\tto get more information");
49
System.out.println(" jinfo --help\tto get more information");
50
System.out.println(" jsnap --help\tto get more information");
51
return false;
52
}
53
54
private static boolean commonHelp(String mode) {
55
return commonHelp(mode, false);
56
}
57
58
private static boolean commonHelpWithConnect(String mode) {
59
return commonHelp(mode, true);
60
}
61
62
private static boolean commonHelp(String mode, boolean canConnectToRemote) {
63
// --pid <pid>
64
// --exe <exe>
65
// --core <core>
66
// --connect [<id>@]<host>[:registryport]
67
System.out.println(" --pid <pid> To attach to and operate on the given live process.");
68
System.out.println(" --core <corefile> To operate on the given core file.");
69
System.out.println(" --exe <executable for corefile>");
70
if (canConnectToRemote) {
71
System.out.println(" --connect [<serverid>@]<host>[:registryport][/servername] To connect to a remote debug server (debugd).");
72
}
73
System.out.println();
74
System.out.println(" The --core and --exe options must be set together to give the core");
75
System.out.println(" file, and associated executable, to operate on. They can use");
76
System.out.println(" absolute or relative paths.");
77
System.out.println(" The --pid option can be set to operate on a live process.");
78
if (canConnectToRemote) {
79
System.out.println(" The --connect option can be set to connect to a debug server (debugd).");
80
System.out.println(" --core, --pid, and --connect are mutually exclusive.");
81
} else {
82
System.out.println(" --core and --pid are mutually exclusive.");
83
}
84
System.out.println();
85
System.out.println(" Examples: jhsdb " + mode + " --pid 1234");
86
System.out.println(" or jhsdb " + mode + " --core ./core.1234 --exe ./myexe");
87
if (canConnectToRemote) {
88
System.out.println(" or jhsdb " + mode + " --connect serverid@debugserver:1234/servername");
89
}
90
return false;
91
}
92
93
private static boolean debugdHelp() {
94
System.out.println(" --serverid <id> A unique identifier for this debugd server.");
95
System.out.println(" --servername <name> Instance name of debugd server.");
96
System.out.println(" --rmiport <port> Sets the port number to which the RMI connector is bound." +
97
" If not specified a random available port is used.");
98
System.out.println(" --registryport <port> Sets the RMI registry port." +
99
" This option overrides the system property 'sun.jvm.hotspot.rmi.port'. If not specified," +
100
" the system property is used. If the system property is not set, the default port 1099 is used.");
101
System.out.println(" --disable-registry Do not start RMI registry (use already started RMI registry)");
102
System.out.println(" --hostname <hostname> Sets the hostname the RMI connector is bound. The value could" +
103
" be a hostname or an IPv4/IPv6 address. This option overrides the system property" +
104
" 'java.rmi.server.hostname'. If not specified, the system property is used. If the system" +
105
" property is not set, a system hostname is used.");
106
return commonHelp("debugd");
107
}
108
109
private static boolean jinfoHelp() {
110
// --flags -> -flags
111
// --sysprops -> -sysprops
112
System.out.println(" --flags To print VM flags.");
113
System.out.println(" --sysprops To print Java System properties.");
114
System.out.println(" <no option> To print both of the above.");
115
return commonHelpWithConnect("jinfo");
116
}
117
118
private static boolean jmapHelp() {
119
// --heap -> -heap
120
// --binaryheap -> -heap:format=b
121
// --histo -> -histo
122
// --clstats -> -clstats
123
// --finalizerinfo -> -finalizerinfo
124
125
System.out.println(" <no option> To print same info as Solaris pmap.");
126
System.out.println(" --heap To print java heap summary.");
127
System.out.println(" --binaryheap To dump java heap in hprof binary format.");
128
System.out.println(" --dumpfile <name> The name of the dump file. Only valid with --binaryheap.");
129
System.out.println(" --gz <1-9> The compression level for gzipped dump file. Only valid with --binaryheap.");
130
System.out.println(" --histo To print histogram of java object heap.");
131
System.out.println(" --clstats To print class loader statistics.");
132
System.out.println(" --finalizerinfo To print information on objects awaiting finalization.");
133
return commonHelpWithConnect("jmap");
134
}
135
136
private static boolean jstackHelp() {
137
// --locks -> -l
138
// --mixed -> -m
139
System.out.println(" --locks To print java.util.concurrent locks.");
140
System.out.println(" --mixed To print both Java and native frames (mixed mode).");
141
return commonHelpWithConnect("jstack");
142
}
143
144
private static boolean jsnapHelp() {
145
System.out.println(" --all To print all performance counters.");
146
return commonHelpWithConnect("jsnap");
147
}
148
149
private static boolean toolHelp(String toolName) {
150
switch (toolName) {
151
case "jstack":
152
return jstackHelp();
153
case "jinfo":
154
return jinfoHelp();
155
case "jmap":
156
return jmapHelp();
157
case "jsnap":
158
return jsnapHelp();
159
case "debugd":
160
return debugdHelp();
161
case "hsdb":
162
case "clhsdb":
163
return commonHelpWithConnect(toolName);
164
default:
165
return launcherHelp();
166
}
167
}
168
169
private static final String NO_REMOTE = null;
170
171
private static String[] buildAttachArgs(Map<String, String> newArgMap,
172
boolean allowEmpty) {
173
String pid = newArgMap.remove("pid");
174
String exe = newArgMap.remove("exe");
175
String core = newArgMap.remove("core");
176
String connect = newArgMap.remove("connect");
177
if (!allowEmpty && (pid == null) && (exe == null) && (connect == NO_REMOTE)) {
178
throw new SAGetoptException("You have to set --pid or --exe or --connect.");
179
}
180
181
List<String> newArgs = new ArrayList<>();
182
for (var entry : newArgMap.entrySet()) {
183
newArgs.add(entry.getKey());
184
if (entry.getValue() != null) {
185
newArgs.add(entry.getValue());
186
}
187
}
188
189
if (pid != null) { // Attach to live process
190
if (exe != null) {
191
throw new SAGetoptException("Unnecessary argument: --exe");
192
} else if (core != null) {
193
throw new SAGetoptException("Unnecessary argument: --core");
194
} else if (connect != NO_REMOTE) {
195
throw new SAGetoptException("Unnecessary argument: --connect");
196
} else if (!pid.matches("^\\d+$")) {
197
throw new SAGetoptException("Invalid pid: " + pid);
198
}
199
200
newArgs.add(pid);
201
} else if (exe != null) {
202
if (connect != NO_REMOTE) {
203
throw new SAGetoptException("Unnecessary argument: --connect");
204
} else if (exe.length() == 0) {
205
throw new SAGetoptException("You have to set --exe.");
206
}
207
208
newArgs.add(exe);
209
210
if ((core == null) || (core.length() == 0)) {
211
throw new SAGetoptException("You have to set --core.");
212
}
213
214
newArgs.add(core);
215
} else if (connect != NO_REMOTE) {
216
newArgs.add(connect);
217
}
218
219
return newArgs.toArray(new String[0]);
220
}
221
222
/**
223
* This method converts jhsdb-style options (oldArgs) to old fashioned
224
* style. SALauncher delegates the work to the entry point of each tool.
225
* Thus we need to convert the arguments.
226
* For example, `jhsdb jstack --mixed` needs to be converted to `jstack -m`.
227
*
228
* longOptsMap holds the rule how this method should convert the args.
229
* The key is the name of jhsdb style, the value is the name of
230
* old fashioned style. If you want to convert mixed option in jstack,
231
* you need to set "mixed" to the key, and to set "-m" to the value
232
* in longOptsMap. If the option have the value, you need to add "=" to
233
* the key like "exe=".
234
*
235
* You also can set the options which cannot be mapped to old fashioned
236
* arguments. For example, `jhsdb jmap --binaryheap` cannot be mapped to
237
* `jmap` option directly. But you set it to longOptsMap, then you can know
238
* the user sets "binaryheap" option, and SALauncher should set
239
* "-heap:format:b" to jmap option.
240
*
241
* This method returns the map of the old fashioned key/val pairs.
242
* It can be used to build args in string array at buildAttachArgs().
243
*/
244
private static Map<String, String> parseOptions(String[] oldArgs,
245
Map<String, String> longOptsMap) {
246
SAGetopt sg = new SAGetopt(oldArgs);
247
String[] longOpts = longOptsMap.keySet().toArray(new String[0]);
248
Map<String, String> newArgMap = new HashMap<>();
249
250
/*
251
* Parse each jhsdb-style option via SAGetopt.
252
* SAGetopt parses and validates the argument. If the user passes invalid
253
* option, SAGetoptException will be occurred at SAGetopt::next.
254
* Thus there is no need to validate it here.
255
*
256
* We can get option value via SAGetopt::get. If jhsdb-style option has
257
* '=' at the tail, we put old fashioned option with it to newArgMap.
258
*/
259
String s;
260
while ((s = sg.next(null, longOpts)) != null) {
261
var val = longOptsMap.get(s);
262
if (val != null) {
263
newArgMap.put(val, null);
264
} else {
265
val = longOptsMap.get(s + "=");
266
if (val != null) {
267
newArgMap.put(val, sg.getOptarg());
268
}
269
}
270
}
271
272
return newArgMap;
273
}
274
275
private static void runCLHSDB(String[] oldArgs) {
276
Map<String, String> longOptsMap = Map.of("exe=", "exe",
277
"core=", "core",
278
"pid=", "pid",
279
"connect=", "connect");
280
Map<String, String> newArgMap = parseOptions(oldArgs, longOptsMap);
281
CLHSDB.main(buildAttachArgs(newArgMap, true));
282
}
283
284
private static void runHSDB(String[] oldArgs) {
285
Map<String, String> longOptsMap = Map.of("exe=", "exe",
286
"core=", "core",
287
"pid=", "pid",
288
"connect=", "connect");
289
Map<String, String> newArgMap = parseOptions(oldArgs, longOptsMap);
290
HSDB.main(buildAttachArgs(newArgMap, true));
291
}
292
293
private static void runJSTACK(String[] oldArgs) {
294
Map<String, String> longOptsMap = Map.of("exe=", "exe",
295
"core=", "core",
296
"pid=", "pid",
297
"connect=", "connect",
298
"mixed", "-m",
299
"locks", "-l");
300
Map<String, String> newArgMap = parseOptions(oldArgs, longOptsMap);
301
JStack jstack = new JStack(false, false);
302
jstack.runWithArgs(buildAttachArgs(newArgMap, false));
303
}
304
305
private static void runJMAP(String[] oldArgs) {
306
Map<String, String> longOptsMap = Map.ofEntries(
307
Map.entry("exe=", "exe"),
308
Map.entry("core=", "core"),
309
Map.entry("pid=", "pid"),
310
Map.entry("connect=", "connect"),
311
Map.entry("heap", "-heap"),
312
Map.entry("binaryheap", "binaryheap"),
313
Map.entry("dumpfile=", "dumpfile"),
314
Map.entry("gz=", "gz"),
315
Map.entry("histo", "-histo"),
316
Map.entry("clstats", "-clstats"),
317
Map.entry("finalizerinfo", "-finalizerinfo"));
318
Map<String, String> newArgMap = parseOptions(oldArgs, longOptsMap);
319
320
boolean requestHeapdump = newArgMap.containsKey("binaryheap");
321
String dumpfile = newArgMap.get("dumpfile");
322
String gzLevel = newArgMap.get("gz");
323
String command = "-heap:format=b";
324
if (!requestHeapdump && (dumpfile != null)) {
325
throw new IllegalArgumentException("Unexpected argument: dumpfile");
326
}
327
if (requestHeapdump) {
328
if (gzLevel != null) {
329
command += ",gz=" + gzLevel;
330
}
331
if (dumpfile != null) {
332
command += ",file=" + dumpfile;
333
}
334
newArgMap.put(command, null);
335
}
336
337
newArgMap.remove("binaryheap");
338
newArgMap.remove("dumpfile");
339
newArgMap.remove("gz");
340
JMap.main(buildAttachArgs(newArgMap, false));
341
}
342
343
private static void runJINFO(String[] oldArgs) {
344
Map<String, String> longOptsMap = Map.of("exe=", "exe",
345
"core=", "core",
346
"pid=", "pid",
347
"connect=", "connect",
348
"flags", "-flags",
349
"sysprops", "-sysprops");
350
Map<String, String> newArgMap = parseOptions(oldArgs, longOptsMap);
351
JInfo.main(buildAttachArgs(newArgMap, false));
352
}
353
354
private static void runJSNAP(String[] oldArgs) {
355
Map<String, String> longOptsMap = Map.of("exe=", "exe",
356
"core=", "core",
357
"pid=", "pid",
358
"connect=", "connect",
359
"all", "-a");
360
Map<String, String> newArgMap = parseOptions(oldArgs, longOptsMap);
361
JSnap.main(buildAttachArgs(newArgMap, false));
362
}
363
364
private static void runDEBUGD(String[] args) {
365
// By default SA agent classes prefer Windows process debugger
366
// to windbg debugger. SA expects special properties to be set
367
// to choose other debuggers. We will set those here before
368
// attaching to SA agent.
369
System.setProperty("sun.jvm.hotspot.debugger.useWindbgDebugger", "true");
370
371
Map<String, String> longOptsMap = Map.of("exe=", "exe",
372
"core=", "core",
373
"pid=", "pid",
374
"serverid=", "serverid",
375
"rmiport=", "rmiport",
376
"registryport=", "registryport",
377
"disable-registry", "disable-registry",
378
"hostname=", "hostname",
379
"servername=", "servername");
380
381
Map<String, String> argMap = parseOptions(args, longOptsMap);
382
383
// Run the basic check for the options. If the check fails
384
// SAGetoptException will be thrown
385
buildAttachArgs(new HashMap<>(argMap), false);
386
387
String serverID = argMap.get("serverid");
388
String rmiPortString = argMap.get("rmiport");
389
String registryPort = argMap.get("registryport");
390
String host = argMap.get("hostname");
391
String javaExecutableName = argMap.get("exe");
392
String coreFileName = argMap.get("core");
393
String pidString = argMap.get("pid");
394
String serverName = argMap.get("servername");
395
396
// Set RMI registry port, if specified
397
if (registryPort != null) {
398
try {
399
Integer.parseInt(registryPort);
400
} catch (NumberFormatException ex) {
401
throw new SAGetoptException("Invalid registry port: " + registryPort);
402
}
403
System.setProperty("sun.jvm.hotspot.rmi.port", registryPort);
404
}
405
406
// Disable RMI registry if specified
407
if (argMap.containsKey("disable-registry")) {
408
System.setProperty("sun.jvm.hotspot.rmi.startRegistry", "false");
409
}
410
411
// Set RMI connector hostname, if specified
412
if (host != null && !host.trim().isEmpty()) {
413
System.setProperty("java.rmi.server.hostname", host);
414
}
415
416
// Set RMI connector port, if specified
417
int rmiPort = 0;
418
if (rmiPortString != null) {
419
try {
420
rmiPort = Integer.parseInt(rmiPortString);
421
} catch (NumberFormatException ex) {
422
throw new SAGetoptException("Invalid RMI connector port: " + rmiPortString);
423
}
424
}
425
426
final HotSpotAgent agent = new HotSpotAgent();
427
428
if (pidString != null) {
429
int pid = 0;
430
try {
431
pid = Integer.parseInt(pidString);
432
} catch (NumberFormatException ex) {
433
throw new SAGetoptException("Invalid pid: " + pidString);
434
}
435
System.err.println("Attaching to process ID " + pid + " and starting RMI services," +
436
" please wait...");
437
try {
438
agent.startServer(pid, serverID, serverName, rmiPort);
439
} catch (DebuggerException e) {
440
System.err.print("Error attaching to process or starting server: ");
441
e.printStackTrace();
442
System.exit(1);
443
} catch (NumberFormatException ex) {
444
throw new SAGetoptException("Invalid pid: " + pid);
445
}
446
} else if (javaExecutableName != null) {
447
System.err.println("Attaching to core " + coreFileName +
448
" from executable " + javaExecutableName + " and starting RMI services, please wait...");
449
try {
450
agent.startServer(javaExecutableName, coreFileName, serverID, serverName, rmiPort);
451
} catch (DebuggerException e) {
452
System.err.print("Error attaching to core file or starting server: ");
453
e.printStackTrace();
454
System.exit(1);
455
}
456
}
457
// shutdown hook to clean-up the server in case of forced exit.
458
Runtime.getRuntime().addShutdownHook(new java.lang.Thread(agent::shutdownServer));
459
System.err.println("Debugger attached and RMI services started." + ((rmiPortString != null) ?
460
(" RMI connector is bound to port " + rmiPort + ".") : ""));
461
462
}
463
464
// Key: tool name, Value: launcher method
465
private static Map<String, Consumer<String[]>> toolMap =
466
Map.of("clhsdb", SALauncher::runCLHSDB,
467
"hsdb", SALauncher::runHSDB,
468
"jstack", SALauncher::runJSTACK,
469
"jmap", SALauncher::runJMAP,
470
"jinfo", SALauncher::runJINFO,
471
"jsnap", SALauncher::runJSNAP,
472
"debugd", SALauncher::runDEBUGD);
473
474
public static void main(String[] args) {
475
// Provide a help
476
if (args.length == 0) {
477
launcherHelp();
478
return;
479
}
480
// No arguments imply help for debugd, jstack, jmap, jinfo but launch clhsdb and hsdb
481
if (args.length == 1 && !args[0].equals("clhsdb") && !args[0].equals("hsdb")) {
482
toolHelp(args[0]);
483
return;
484
}
485
486
for (String arg : args) {
487
if (arg.equals("-h") || arg.equals("-help") || arg.equals("--help")) {
488
toolHelp(args[0]);
489
return;
490
}
491
}
492
493
String[] oldArgs = Arrays.copyOfRange(args, 1, args.length);
494
495
try {
496
var func = toolMap.get(args[0]);
497
if (func == null) {
498
throw new SAGetoptException("Unknown tool: " + args[0]);
499
} else {
500
func.accept(oldArgs);
501
}
502
} catch (SAGetoptException e) {
503
System.err.println(e.getMessage());
504
toolHelp(args[0]);
505
// Exit with error status
506
System.exit(1);
507
}
508
}
509
}
510
511