mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-20 23:36:18 +00:00
2091 lines
84 KiB
Java
2091 lines
84 KiB
Java
/*
|
|
* Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*
|
|
*/
|
|
|
|
package sun.jvm.hotspot;
|
|
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.BufferedReader;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.io.PrintStream;
|
|
import java.util.ArrayDeque;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Comparator;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.Objects;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
import sun.jvm.hotspot.code.CodeBlob;
|
|
import sun.jvm.hotspot.code.CodeCacheVisitor;
|
|
import sun.jvm.hotspot.code.NMethod;
|
|
import sun.jvm.hotspot.debugger.Address;
|
|
import sun.jvm.hotspot.debugger.OopHandle;
|
|
import sun.jvm.hotspot.classfile.ClassLoaderDataGraph;
|
|
import sun.jvm.hotspot.memory.FileMapInfo;
|
|
import sun.jvm.hotspot.memory.Universe;
|
|
import sun.jvm.hotspot.gc.shared.CollectedHeap;
|
|
import sun.jvm.hotspot.gc.g1.G1CollectedHeap;
|
|
import sun.jvm.hotspot.oops.DefaultHeapVisitor;
|
|
import sun.jvm.hotspot.oops.HeapVisitor;
|
|
import sun.jvm.hotspot.oops.InstanceKlass;
|
|
import sun.jvm.hotspot.oops.Klass;
|
|
import sun.jvm.hotspot.oops.Metadata;
|
|
import sun.jvm.hotspot.oops.Method;
|
|
import sun.jvm.hotspot.oops.Oop;
|
|
import sun.jvm.hotspot.oops.RawHeapVisitor;
|
|
import sun.jvm.hotspot.oops.Symbol;
|
|
import sun.jvm.hotspot.oops.UnknownOopException;
|
|
import sun.jvm.hotspot.runtime.CompiledVFrame;
|
|
import sun.jvm.hotspot.runtime.CompilerThread;
|
|
import sun.jvm.hotspot.runtime.JavaThread;
|
|
import sun.jvm.hotspot.runtime.JavaVFrame;
|
|
import sun.jvm.hotspot.runtime.Threads;
|
|
import sun.jvm.hotspot.runtime.VM;
|
|
import sun.jvm.hotspot.tools.ObjectHistogram;
|
|
import sun.jvm.hotspot.tools.JMap;
|
|
import sun.jvm.hotspot.tools.PMap;
|
|
import sun.jvm.hotspot.tools.PStack;
|
|
import sun.jvm.hotspot.tools.StackTrace;
|
|
import sun.jvm.hotspot.tools.SysPropsDumper;
|
|
import sun.jvm.hotspot.tools.jcore.ClassDump;
|
|
import sun.jvm.hotspot.tools.jcore.ClassFilter;
|
|
import sun.jvm.hotspot.tools.jcore.ClassWriter;
|
|
import sun.jvm.hotspot.types.CIntegerType;
|
|
import sun.jvm.hotspot.types.Field;
|
|
import sun.jvm.hotspot.types.Type;
|
|
import sun.jvm.hotspot.types.basic.BasicType;
|
|
import sun.jvm.hotspot.ui.classbrowser.HTMLGenerator;
|
|
import sun.jvm.hotspot.ui.tree.CTypeTreeNodeAdapter;
|
|
import sun.jvm.hotspot.ui.tree.OopTreeNodeAdapter;
|
|
import sun.jvm.hotspot.ui.tree.SimpleTreeNode;
|
|
import sun.jvm.hotspot.utilities.AddressOps;
|
|
import sun.jvm.hotspot.utilities.Assert;
|
|
import sun.jvm.hotspot.utilities.HeapProgressThunk;
|
|
import sun.jvm.hotspot.utilities.LivenessPathElement;
|
|
import sun.jvm.hotspot.utilities.MethodArray;
|
|
import sun.jvm.hotspot.utilities.PointerFinder;
|
|
import sun.jvm.hotspot.utilities.PointerLocation;
|
|
import sun.jvm.hotspot.utilities.ReversePtrs;
|
|
import sun.jvm.hotspot.utilities.ReversePtrsAnalysis;
|
|
import sun.jvm.hotspot.utilities.RobustOopDeterminator;
|
|
import sun.jvm.hotspot.utilities.SystemDictionaryHelper;
|
|
|
|
public class CommandProcessor {
|
|
|
|
volatile boolean quit;
|
|
|
|
public abstract static class DebuggerInterface {
|
|
public abstract HotSpotAgent getAgent();
|
|
public abstract boolean isAttached();
|
|
public abstract void attach(int pid);
|
|
public abstract void attach(String java, String core);
|
|
public abstract void attach(String debugServerName);
|
|
public abstract void detach();
|
|
public abstract void reattach();
|
|
}
|
|
|
|
public static class BootFilter implements ClassFilter {
|
|
public boolean canInclude(InstanceKlass kls) {
|
|
return kls.getClassLoader() == null;
|
|
}
|
|
}
|
|
|
|
public static class NonBootFilter implements ClassFilter {
|
|
private HashMap<Symbol, InstanceKlass> emitted = new HashMap<>();
|
|
public boolean canInclude(InstanceKlass kls) {
|
|
if (kls.getClassLoader() == null) return false;
|
|
if (emitted.get(kls.getName()) != null) {
|
|
// Since multiple class loaders are being shoved
|
|
// together duplicate classes are a possibility. For
|
|
// now just ignore them.
|
|
return false;
|
|
}
|
|
emitted.put(kls.getName(), kls);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static class Tokens {
|
|
final String input;
|
|
int i;
|
|
String[] tokens;
|
|
int length;
|
|
|
|
String[] splitWhitespace(String cmd) {
|
|
String[] t = cmd.split("\\s");
|
|
if (t.length == 1 && t[0].length() == 0) {
|
|
return new String[0];
|
|
}
|
|
return t;
|
|
}
|
|
|
|
void add(String s, ArrayList<String> t) {
|
|
if (s.length() > 0) {
|
|
t.add(s);
|
|
}
|
|
}
|
|
|
|
Tokens(String cmd) {
|
|
input = cmd;
|
|
// check for quoting
|
|
int quote = cmd.indexOf('"');
|
|
ArrayList<String> t = new ArrayList<>();
|
|
if (quote != -1) {
|
|
while (cmd.length() > 0) {
|
|
if (quote != -1) {
|
|
int endquote = cmd.indexOf('"', quote + 1);
|
|
if (endquote == -1) {
|
|
throw new RuntimeException("mismatched quotes: " + input);
|
|
}
|
|
|
|
String before = cmd.substring(0, quote).trim();
|
|
String quoted = cmd.substring(quote + 1, endquote);
|
|
cmd = cmd.substring(endquote + 1).trim();
|
|
if (before.length() > 0) {
|
|
String[] w = splitWhitespace(before);
|
|
for (int i = 0; i < w.length; i++) {
|
|
add(w[i], t);
|
|
}
|
|
}
|
|
add(quoted, t);
|
|
quote = cmd.indexOf('"');
|
|
} else {
|
|
String[] w = splitWhitespace(cmd);
|
|
for (int i = 0; i < w.length; i++) {
|
|
add(w[i], t);
|
|
}
|
|
cmd = "";
|
|
|
|
}
|
|
}
|
|
} else {
|
|
String[] w = splitWhitespace(cmd);
|
|
for (int i = 0; i < w.length; i++) {
|
|
add(w[i], t);
|
|
}
|
|
}
|
|
tokens = t.toArray(new String[0]);
|
|
i = 0;
|
|
length = tokens.length;
|
|
|
|
//for (int i = 0; i < tokens.length; i++) {
|
|
// System.out.println("\"" + tokens[i] + "\"");
|
|
//}
|
|
}
|
|
|
|
String nextToken() {
|
|
return tokens[i++];
|
|
}
|
|
boolean hasMoreTokens() {
|
|
return i < length;
|
|
}
|
|
int countTokens() {
|
|
return length - i;
|
|
}
|
|
void trim(int n) {
|
|
if (length >= n) {
|
|
length -= n;
|
|
} else {
|
|
throw new IndexOutOfBoundsException(String.valueOf(n));
|
|
}
|
|
}
|
|
String join(String sep) {
|
|
StringBuilder result = new StringBuilder();
|
|
for (int w = i; w < length; w++) {
|
|
result.append(tokens[w]);
|
|
if (w + 1 < length) {
|
|
result.append(sep);
|
|
}
|
|
}
|
|
return result.toString();
|
|
}
|
|
|
|
String at(int i) {
|
|
Objects.checkIndex(i, length);
|
|
return tokens[i];
|
|
}
|
|
}
|
|
|
|
|
|
abstract class Command {
|
|
Command(String n, String u, boolean ok) {
|
|
name = n;
|
|
usage = u;
|
|
okIfDisconnected = ok;
|
|
}
|
|
|
|
Command(String n, boolean ok) {
|
|
name = n;
|
|
usage = n;
|
|
okIfDisconnected = ok;
|
|
}
|
|
|
|
final String name;
|
|
final String usage;
|
|
final boolean okIfDisconnected;
|
|
abstract void doit(Tokens t);
|
|
void usage() {
|
|
out.println("Usage: " + usage);
|
|
}
|
|
|
|
void printNode(SimpleTreeNode node) {
|
|
int count = node.getChildCount();
|
|
for (int i = 0; i < count; i++) {
|
|
try {
|
|
SimpleTreeNode field = node.getChild(i);
|
|
out.println(field);
|
|
} catch (Exception e) {
|
|
out.println();
|
|
out.println("Error: " + e);
|
|
if (verboseExceptions) {
|
|
e.printStackTrace(out);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void quote(String s) {
|
|
if (!s.contains(" ")) {
|
|
out.print(s);
|
|
} else {
|
|
out.print("\"");
|
|
out.print(s);
|
|
out.print("\"");
|
|
}
|
|
}
|
|
|
|
void dumpType(Type type) {
|
|
out.print("type ");
|
|
quote(type.getName());
|
|
out.print(" ");
|
|
if (type.getSuperclass() != null) {
|
|
quote(type.getSuperclass().getName());
|
|
out.print(" ");
|
|
} else {
|
|
out.print("null ");
|
|
}
|
|
out.print(type.isOopType());
|
|
out.print(" ");
|
|
if (type.isCIntegerType()) {
|
|
out.print("true ");
|
|
out.print(((CIntegerType)type).isUnsigned());
|
|
out.print(" ");
|
|
} else {
|
|
out.print("false false ");
|
|
}
|
|
out.print(type.getSize());
|
|
out.println();
|
|
}
|
|
|
|
void dumpFields(Type type) {
|
|
dumpFields(type, true);
|
|
}
|
|
|
|
void dumpFields(Type type, boolean allowStatic) {
|
|
Iterator i = type.getFields();
|
|
while (i.hasNext()) {
|
|
Field f = (Field) i.next();
|
|
if (!allowStatic && f.isStatic()) continue;
|
|
out.print("field ");
|
|
quote(type.getName());
|
|
out.print(" ");
|
|
out.print(f.getName());
|
|
out.print(" ");
|
|
quote(f.getType().getName());
|
|
out.print(" ");
|
|
out.print(f.isStatic());
|
|
out.print(" ");
|
|
if (f.isStatic()) {
|
|
out.print("0 ");
|
|
out.print(f.getStaticFieldAddress());
|
|
} else {
|
|
out.print(f.getOffset());
|
|
out.print(" 0x0");
|
|
}
|
|
out.println();
|
|
}
|
|
}
|
|
|
|
|
|
Address lookup(String symbol) {
|
|
if (symbol.contains("::")) {
|
|
String[] parts = symbol.split("::");
|
|
StringBuilder mangled = new StringBuilder("__1c");
|
|
for (int i = 0; i < parts.length; i++) {
|
|
int len = parts[i].length();
|
|
if (len >= 26) {
|
|
mangled.append((char)('a' + (len / 26)));
|
|
len = len % 26;
|
|
}
|
|
mangled.append((char)('A' + len));
|
|
mangled.append(parts[i]);
|
|
}
|
|
mangled.append("_");
|
|
symbol = mangled.toString();
|
|
}
|
|
return VM.getVM().getDebugger().lookup(null, symbol);
|
|
}
|
|
|
|
Address parseAddress(String addr) {
|
|
return VM.getVM().getDebugger().parseAddress(addr);
|
|
}
|
|
|
|
String fillHexString(Address a, int width) {
|
|
String s = "0x0";
|
|
if (a != null) {
|
|
s = a.toString();
|
|
}
|
|
if (s.length() != width) {
|
|
return s.substring(0, 2) + "000000000000000000000".substring(0, width - s.length()) + s.substring(2);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
class AddressRange {
|
|
private Address start;
|
|
private Address end;
|
|
AddressRange(Address start, Address end) {
|
|
this.start = start;
|
|
this.end = end;
|
|
}
|
|
Address getStart() {return start;}
|
|
Address getEnd() {return end;}
|
|
}
|
|
|
|
// Parses either address[/count] or address,address into address start/end values
|
|
AddressRange parseAddressRange(String arg, int formatSize) {
|
|
Pattern args1 = Pattern.compile("^(0x[0-9a-f]+)(/([0-9]*))?$");
|
|
Pattern args2 = Pattern.compile("^(0x[0-9a-f]+),(0x[0-9a-f]+)$");
|
|
Matcher m1 = args1.matcher(arg);
|
|
Matcher m2 = args2.matcher(arg);
|
|
Address start = null;
|
|
Address end = null;
|
|
|
|
if (m1.matches()) {
|
|
start = VM.getVM().getDebugger().parseAddress(m1.group(1));
|
|
int count = 1;
|
|
if (m1.group(2) != null) {
|
|
count = Integer.parseInt(m1.group(3));
|
|
}
|
|
end = start.addOffsetTo(count * formatSize);
|
|
return new AddressRange(start, end);
|
|
} else if (m2.matches()) {
|
|
start = VM.getVM().getDebugger().parseAddress(m2.group(1));
|
|
end = VM.getVM().getDebugger().parseAddress(m2.group(2));
|
|
return new AddressRange(start, end);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private final Command[] commandList = {
|
|
new Command("reattach", true) {
|
|
public void doit(Tokens t) {
|
|
int tokens = t.countTokens();
|
|
if (tokens != 0) {
|
|
usage();
|
|
return;
|
|
}
|
|
preAttach();
|
|
debugger.reattach();
|
|
postAttach();
|
|
}
|
|
},
|
|
new Command("attach", "attach pid | exec core | remote_server", true) {
|
|
public void doit(Tokens t) {
|
|
int tokens = t.countTokens();
|
|
if (tokens == 1) {
|
|
preAttach();
|
|
String arg = t.nextToken();
|
|
try {
|
|
// Attempt to attach as a PID
|
|
debugger.attach(Integer.parseInt(arg));
|
|
} catch (NumberFormatException e) {
|
|
// Attempt to connect to remote debug server
|
|
debugger.attach(arg);
|
|
}
|
|
postAttach();
|
|
} else if (tokens == 2) {
|
|
preAttach();
|
|
debugger.attach(t.nextToken(), t.nextToken());
|
|
postAttach();
|
|
} else {
|
|
usage();
|
|
}
|
|
}
|
|
},
|
|
new Command("detach", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 0) {
|
|
usage();
|
|
} else {
|
|
debugger.detach();
|
|
}
|
|
}
|
|
},
|
|
new Command("examine", "examine { address[/count] | address,address }", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
} else {
|
|
String arg = t.nextToken();
|
|
int formatSize = (int)VM.getVM().getAddressSize();
|
|
AddressRange addressRange = parseAddressRange(arg, formatSize);
|
|
if (addressRange == null) {
|
|
usage();
|
|
return;
|
|
}
|
|
Address start = addressRange.getStart();
|
|
Address end = addressRange.getEnd();
|
|
|
|
int line = 80;
|
|
int formatWidth = formatSize * 8 / 4 + 2;
|
|
|
|
out.print(fillHexString(start, formatWidth));
|
|
out.print(": ");
|
|
int width = line - formatWidth - 2;
|
|
|
|
boolean needsPrintln = true;
|
|
while (start != null && start.lessThan(end)) {
|
|
Address val = start.getAddressAt(0);
|
|
out.print(fillHexString(val, formatWidth));
|
|
needsPrintln = true;
|
|
width -= formatWidth;
|
|
start = start.addOffsetTo(formatSize);
|
|
if (width <= formatWidth) {
|
|
out.println();
|
|
needsPrintln = false;
|
|
if (start.lessThan(end)) {
|
|
out.print(fillHexString(start, formatWidth));
|
|
out.print(": ");
|
|
width = line - formatWidth - 2;
|
|
}
|
|
} else {
|
|
out.print(" ");
|
|
width -= 1;
|
|
}
|
|
}
|
|
if (needsPrintln) {
|
|
out.println();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new Command("mem", "mem [-v] { address[/count] | address,address }", false) {
|
|
public void doit(Tokens t) {
|
|
int formatSize = (int)VM.getVM().getAddressSize();
|
|
boolean verbose = false;
|
|
String arg;
|
|
|
|
if (t.countTokens() == 2) {
|
|
arg = t.nextToken();
|
|
if (arg.equals("-v")) {
|
|
verbose = true;
|
|
} else {
|
|
usage();
|
|
return;
|
|
}
|
|
}
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
return;
|
|
}
|
|
|
|
arg = t.nextToken();
|
|
AddressRange addressRange = parseAddressRange(arg, formatSize);
|
|
if (addressRange == null) {
|
|
usage();
|
|
return;
|
|
}
|
|
Address start = addressRange.getStart();
|
|
Address end = addressRange.getEnd();
|
|
|
|
if (verbose) {
|
|
// Do the equivalent of a findpc on the start address.
|
|
PointerLocation loc = PointerFinder.find(start);
|
|
loc.printOn(out);
|
|
}
|
|
|
|
int formatWidth = formatSize * 8 / 4 + 2;
|
|
while (start != null && start.lessThan(end)) {
|
|
out.print(fillHexString(start, formatWidth));
|
|
out.print(": ");
|
|
Address val = start.getAddressAt(0);
|
|
out.print(fillHexString(val, formatWidth));
|
|
if (verbose) {
|
|
// If we know what this is a pointer to, then print additional information.
|
|
PointerLocation loc = PointerFinder.find(val);
|
|
if (!loc.isUnknown()) {
|
|
out.print(" ");
|
|
loc.printOn(out, false, false);
|
|
} else {
|
|
out.println();
|
|
}
|
|
} else {
|
|
out.println();
|
|
}
|
|
start = start.addOffsetTo(formatSize);
|
|
}
|
|
}
|
|
},
|
|
new Command("buildreplayjars", "buildreplayjars [ all | app | boot ] | [ prefix ]", false) {
|
|
// This is used to dump jar files of all the classes
|
|
// loaded in the core. Everything with null classloader
|
|
// will go in boot.jar and everything else will go in
|
|
// app.jar. boot.jar usually not needed, unless changed by jvmti.
|
|
public void doit(Tokens t) {
|
|
int tcount = t.countTokens();
|
|
if (tcount > 2) {
|
|
usage();
|
|
return;
|
|
}
|
|
try {
|
|
String prefix = "";
|
|
String option = "all"; // default
|
|
switch(tcount) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
option = t.nextToken();
|
|
if (!option.equalsIgnoreCase("all") && !option.equalsIgnoreCase("app") &&
|
|
!option.equalsIgnoreCase("root")) {
|
|
prefix = option;
|
|
option = "all";
|
|
}
|
|
break;
|
|
case 2:
|
|
option = t.nextToken();
|
|
prefix = t.nextToken();
|
|
break;
|
|
default:
|
|
usage();
|
|
return;
|
|
}
|
|
if (!option.equalsIgnoreCase("all") && !option.equalsIgnoreCase("app") &&
|
|
!option.equalsIgnoreCase("boot")) {
|
|
usage();
|
|
return;
|
|
}
|
|
ClassDump cd = new ClassDump();
|
|
if (option.equalsIgnoreCase("all") || option.equalsIgnoreCase("boot")) {
|
|
cd.setClassFilter(new BootFilter());
|
|
cd.setJarOutput(prefix + "boot.jar");
|
|
cd.run();
|
|
}
|
|
if (option.equalsIgnoreCase("all") || option.equalsIgnoreCase("app")) {
|
|
cd.setClassFilter(new NonBootFilter());
|
|
cd.setJarOutput(prefix + "app.jar");
|
|
cd.run();
|
|
}
|
|
} catch (IOException ioe) {
|
|
ioe.printStackTrace();
|
|
}
|
|
}
|
|
},
|
|
new Command("findsym", "findsym name", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
} else {
|
|
String result = VM.getVM().getDebugger().findSymbol(t.nextToken());
|
|
out.println(result == null ? "Symbol not found" : result);
|
|
}
|
|
}
|
|
},
|
|
new Command("findpc", "findpc address", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
} else {
|
|
Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
|
|
PointerLocation loc = PointerFinder.find(a);
|
|
loc.printOn(out);
|
|
}
|
|
}
|
|
},
|
|
// "whatis" is just an alias for "findpc". It's kept around for compatibility reasons.
|
|
new Command("whatis", "whatis address", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
} else {
|
|
Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
|
|
PointerLocation loc = PointerFinder.find(a);
|
|
loc.printOn(out);
|
|
}
|
|
}
|
|
},
|
|
new Command("symbol", "symbol address", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
} else {
|
|
Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
|
|
Symbol.create(a).printValueOn(out);
|
|
out.println();
|
|
}
|
|
}
|
|
},
|
|
new Command("flags", "flags [ flag | -nd ]", false) {
|
|
public void doit(Tokens t) {
|
|
int tokens = t.countTokens();
|
|
if (tokens != 0 && tokens != 1) {
|
|
usage();
|
|
} else {
|
|
String name = tokens > 0 ? t.nextToken() : null;
|
|
boolean nonDefault = false;
|
|
if (name != null && name.equals("-nd")) {
|
|
name = null;
|
|
nonDefault = true;
|
|
}
|
|
|
|
VM.Flag[] flags = VM.getVM().getCommandLineFlags();
|
|
if (flags == null) {
|
|
out.println("Command Flag info not available (use 1.4.1_03 or later)!");
|
|
} else {
|
|
boolean printed = false;
|
|
for (int f = 0; f < flags.length; f++) {
|
|
VM.Flag flag = flags[f];
|
|
if (name == null || flag.getName().equals(name)) {
|
|
|
|
if (nonDefault && (flag.getOrigin() == VM.Flags_DEFAULT)) {
|
|
// only print flags which aren't their defaults
|
|
continue;
|
|
}
|
|
out.println(flag.getName() + " = " + flag.getValue() + " " + flag.getOriginString());
|
|
printed = true;
|
|
}
|
|
}
|
|
if (name != null && !printed) {
|
|
out.println("Couldn't find flag: " + name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new Command("help", "help [ command ]", true) {
|
|
public void doit(Tokens t) {
|
|
int tokens = t.countTokens();
|
|
Command cmd = null;
|
|
if (tokens == 1) {
|
|
cmd = findCommand(t.nextToken());
|
|
}
|
|
|
|
if (cmd != null) {
|
|
cmd.usage();
|
|
} else if (tokens == 0) {
|
|
out.println("Available commands:");
|
|
Object[] keys = commands.keySet().toArray();
|
|
Arrays.sort(keys, new Comparator<>() {
|
|
public int compare(Object o1, Object o2) {
|
|
return o1.toString().compareTo(o2.toString());
|
|
}
|
|
});
|
|
for (int i = 0; i < keys.length; i++) {
|
|
out.print(" ");
|
|
out.println(commands.get(keys[i]).usage);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new Command("history", "history", true) {
|
|
public void doit(Tokens t) {
|
|
int tokens = t.countTokens();
|
|
if (tokens != 0 && (tokens != 1 || !t.nextToken().equals("-h"))) {
|
|
usage();
|
|
return;
|
|
}
|
|
boolean printIndex = tokens == 0;
|
|
for (int i = 0; i < history.size(); i++) {
|
|
if (printIndex) out.print(i + " ");
|
|
out.println(history.get(i));
|
|
}
|
|
}
|
|
},
|
|
// decode raw address
|
|
new Command("dis", "dis address [length]", false) {
|
|
public void doit(Tokens t) {
|
|
int tokens = t.countTokens();
|
|
if (tokens != 1 && tokens != 2) {
|
|
usage();
|
|
return;
|
|
}
|
|
String name = t.nextToken();
|
|
Address addr = null;
|
|
int len = 0x10; // default length
|
|
try {
|
|
addr = VM.getVM().getDebugger().parseAddress(name);
|
|
} catch (NumberFormatException e) {
|
|
out.println(e);
|
|
return;
|
|
}
|
|
if (tokens == 2) {
|
|
try {
|
|
len = Integer.parseInt(t.nextToken());
|
|
} catch (NumberFormatException e) {
|
|
out.println(e);
|
|
return;
|
|
}
|
|
}
|
|
HTMLGenerator generator = new HTMLGenerator(false);
|
|
out.println(generator.genHTMLForRawDisassembly(addr, len));
|
|
}
|
|
|
|
},
|
|
// decode codeblob or nmethod
|
|
new Command("disassemble", "disassemble address", false) {
|
|
public void doit(Tokens t) {
|
|
int tokens = t.countTokens();
|
|
if (tokens != 1) {
|
|
usage();
|
|
return;
|
|
}
|
|
String name = t.nextToken();
|
|
Address addr = null;
|
|
try {
|
|
addr = VM.getVM().getDebugger().parseAddress(name);
|
|
} catch (NumberFormatException e) {
|
|
out.println(e);
|
|
return;
|
|
}
|
|
|
|
HTMLGenerator generator = new HTMLGenerator(false);
|
|
out.println(generator.genHTML(addr));
|
|
}
|
|
},
|
|
// print Java bytecode disassembly
|
|
new Command("jdis", "jdis address", false) {
|
|
public void doit(Tokens t) {
|
|
int tokens = t.countTokens();
|
|
if (tokens != 1) {
|
|
usage();
|
|
return;
|
|
}
|
|
Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
|
|
Method m = (Method)Metadata.instantiateWrapperFor(a);
|
|
HTMLGenerator html = new HTMLGenerator(false);
|
|
out.println(html.genHTML(m));
|
|
}
|
|
},
|
|
new Command("revptrs", "revptrs address", false) {
|
|
public void doit(Tokens t) {
|
|
int tokens = t.countTokens();
|
|
if (tokens != 1 && (tokens != 2 || !t.nextToken().equals("-c"))) {
|
|
usage();
|
|
return;
|
|
}
|
|
boolean chase = tokens == 2;
|
|
ReversePtrs revptrs = VM.getVM().getRevPtrs();
|
|
if (revptrs == null) {
|
|
out.println("Computing reverse pointers...");
|
|
ReversePtrsAnalysis analysis = new ReversePtrsAnalysis();
|
|
final boolean[] complete = new boolean[1];
|
|
HeapProgressThunk thunk = new HeapProgressThunk() {
|
|
public void heapIterationFractionUpdate(double d) {}
|
|
public synchronized void heapIterationComplete() {
|
|
complete[0] = true;
|
|
notify();
|
|
}
|
|
};
|
|
analysis.setHeapProgressThunk(thunk);
|
|
analysis.run();
|
|
while (!complete[0]) {
|
|
synchronized (thunk) {
|
|
try {
|
|
thunk.wait();
|
|
} catch (Exception e) {
|
|
}
|
|
}
|
|
}
|
|
revptrs = VM.getVM().getRevPtrs();
|
|
out.println("Done.");
|
|
}
|
|
Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
|
|
if (VM.getVM().getUniverse().heap().isInReserved(a)) {
|
|
OopHandle handle = a.addOffsetToAsOopHandle(0);
|
|
Oop oop = VM.getVM().getObjectHeap().newOop(handle);
|
|
ArrayList ptrs = revptrs.get(oop);
|
|
if (ptrs == null) {
|
|
out.println("no live references to " + a);
|
|
} else {
|
|
if (chase) {
|
|
while (ptrs.size() == 1) {
|
|
LivenessPathElement e = (LivenessPathElement)ptrs.get(0);
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
Oop.printOopValueOn(e.getObj(), new PrintStream(bos));
|
|
out.println(bos.toString());
|
|
ptrs = revptrs.get(e.getObj());
|
|
}
|
|
} else {
|
|
for (int i = 0; i < ptrs.size(); i++) {
|
|
LivenessPathElement e = (LivenessPathElement)ptrs.get(i);
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
Oop.printOopValueOn(e.getObj(), new PrintStream(bos));
|
|
out.println(bos.toString());
|
|
oop = e.getObj();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new Command("printall", "printall", false) {
|
|
// Print every Method for every class loaded.
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 0) {
|
|
usage();
|
|
} else {
|
|
ClassLoaderDataGraph cldg = VM.getVM().getClassLoaderDataGraph();
|
|
cldg.classesDo(new ClassLoaderDataGraph.ClassVisitor() {
|
|
public void visit(Klass k) {
|
|
if (k instanceof InstanceKlass && ((InstanceKlass)k).getConstants().getCache() != null) {
|
|
MethodArray methods = ((InstanceKlass)k).getMethods();
|
|
for (int i = 0; i < methods.length(); i++) {
|
|
Method m = methods.at(i);
|
|
HTMLGenerator gen = new HTMLGenerator(false);
|
|
out.println(gen.genHTML(m));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|
|
},
|
|
new Command("vmstructsdump", "vmstructsdump", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 0) {
|
|
usage();
|
|
return;
|
|
}
|
|
|
|
// Dump a copy of the type database in a form that can
|
|
// be read back.
|
|
Iterator i = agent.getTypeDataBase().getTypes();
|
|
// Make sure the types are emitted in an order than can be read back in
|
|
HashSet<String> emitted = new HashSet<>();
|
|
ArrayDeque<Type> pending = new ArrayDeque<>();
|
|
while (i.hasNext()) {
|
|
Type n = (Type)i.next();
|
|
if (emitted.contains(n.getName())) {
|
|
continue;
|
|
}
|
|
|
|
while (n != null && !emitted.contains(n.getName())) {
|
|
pending.push(n);
|
|
n = n.getSuperclass();
|
|
}
|
|
while (!pending.isEmpty()) {
|
|
n = pending.pop();
|
|
dumpType(n);
|
|
emitted.add(n.getName());
|
|
}
|
|
}
|
|
i = agent.getTypeDataBase().getTypes();
|
|
while (i.hasNext()) {
|
|
dumpFields((Type)i.next(), false);
|
|
}
|
|
}
|
|
},
|
|
|
|
new Command("inspect", "inspect expression", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
} else {
|
|
Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
|
|
SimpleTreeNode node = null;
|
|
if (VM.getVM().getUniverse().heap().isInReserved(a)) {
|
|
OopHandle handle = a.addOffsetToAsOopHandle(0);
|
|
Oop oop = VM.getVM().getObjectHeap().newOop(handle);
|
|
node = new OopTreeNodeAdapter(oop, null);
|
|
|
|
out.println("instance of " + node.getValue() +
|
|
" (size = " + oop.getObjectSize() + ")");
|
|
} else if (VM.getVM().getCodeCache().contains(a)) {
|
|
CodeBlob blob = VM.getVM().getCodeCache().findBlobUnsafe(a);
|
|
a = blob.headerBegin();
|
|
}
|
|
if (node == null) {
|
|
Type type = VM.getVM().getTypeDataBase().guessTypeForAddress(a);
|
|
if (type == null && VM.getVM().isSharingEnabled()) {
|
|
// Check if the value falls in the _md_region
|
|
Address loc1 = a.getAddressAt(0);
|
|
FileMapInfo cdsFileMapInfo = VM.getVM().getFileMapInfo();
|
|
if (cdsFileMapInfo.inCopiedVtableSpace(loc1)) {
|
|
type = cdsFileMapInfo.getTypeForVptrAddress(loc1);
|
|
}
|
|
|
|
}
|
|
if (type != null) {
|
|
out.println("Type is " + type.getName() + " (size of " + type.getSize() + ")");
|
|
node = new CTypeTreeNodeAdapter(a, type, null);
|
|
}
|
|
}
|
|
if (node != null) {
|
|
printNode(node);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new Command("jhisto", "jhisto", false) {
|
|
public void doit(Tokens t) {
|
|
ObjectHistogram histo = new ObjectHistogram();
|
|
histo.run(out, err);
|
|
}
|
|
},
|
|
new Command("jstack", "jstack [-v] [-l]", false) {
|
|
public void doit(Tokens t) {
|
|
boolean verbose = false;
|
|
boolean concurrentLocks = false;
|
|
while (t.countTokens() > 0) {
|
|
String arg = t.nextToken();
|
|
if (arg.equals("-v")) {
|
|
verbose = true;
|
|
} else if (arg.equals("-l")) {
|
|
concurrentLocks = true;
|
|
} else {
|
|
usage();
|
|
return;
|
|
}
|
|
}
|
|
StackTrace jstack = new StackTrace(verbose, concurrentLocks);
|
|
jstack.run(out);
|
|
}
|
|
},
|
|
new Command("print", "print expression", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
} else {
|
|
Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
|
|
HTMLGenerator gen = new HTMLGenerator(false);
|
|
out.println(gen.genHTML(a));
|
|
}
|
|
}
|
|
},
|
|
new Command("printas", "printas type expression", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 2) {
|
|
usage();
|
|
} else {
|
|
Type type = agent.getTypeDataBase().lookupType(t.nextToken());
|
|
Address a = VM.getVM().getDebugger().parseAddress(t.nextToken());
|
|
CTypeTreeNodeAdapter node = new CTypeTreeNodeAdapter(a, type, null);
|
|
|
|
out.println("pointer to " + type + " @ " + a +
|
|
" (size = " + type.getSize() + ")");
|
|
printNode(node);
|
|
}
|
|
}
|
|
},
|
|
new Command("printstatics", "printstatics [ type ]", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() > 1) {
|
|
usage();
|
|
} else {
|
|
if (t.countTokens() == 0) {
|
|
out.println("All known static fields");
|
|
printNode(new CTypeTreeNodeAdapter(agent.getTypeDataBase().getTypes()));
|
|
} else {
|
|
Type type = agent.getTypeDataBase().lookupType(t.nextToken());
|
|
out.println("Static fields of " + type.getName());
|
|
printNode(new CTypeTreeNodeAdapter(type));
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new Command("pmap", "pmap", false) {
|
|
public void doit(Tokens t) {
|
|
PMap pmap = new PMap(debugger.getAgent());
|
|
pmap.run(out, debugger.getAgent().getDebugger());
|
|
}
|
|
},
|
|
new Command("pstack", "pstack [-v] [-l]", false) {
|
|
public void doit(Tokens t) {
|
|
boolean verbose = false;
|
|
boolean concurrentLocks = false;
|
|
while (t.countTokens() > 0) {
|
|
String arg = t.nextToken();
|
|
if (arg.equals("-v")) {
|
|
verbose = true;
|
|
} else if (arg.equals("-l")) {
|
|
concurrentLocks = true;
|
|
} else {
|
|
usage();
|
|
return;
|
|
}
|
|
}
|
|
PStack pstack = new PStack(verbose, concurrentLocks, debugger.getAgent());
|
|
pstack.run(out, debugger.getAgent().getDebugger());
|
|
}
|
|
},
|
|
new Command("quit", true) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 0) {
|
|
usage();
|
|
} else {
|
|
debugger.detach();
|
|
quit = true;
|
|
}
|
|
}
|
|
},
|
|
new Command("echo", "echo [ true | false ]", true) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() == 0) {
|
|
out.println("echo is " + doEcho);
|
|
} else if (t.countTokens() == 1) {
|
|
doEcho = Boolean.parseBoolean(t.nextToken());
|
|
} else {
|
|
usage();
|
|
}
|
|
}
|
|
},
|
|
new Command("versioncheck", "versioncheck [ true | false ]", true) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() == 0) {
|
|
out.println("versioncheck is " +
|
|
(System.getProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck") == null));
|
|
} else if (t.countTokens() == 1) {
|
|
if (Boolean.parseBoolean(t.nextToken())) {
|
|
System.clearProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck");
|
|
} else {
|
|
System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", "true");
|
|
}
|
|
} else {
|
|
usage();
|
|
}
|
|
}
|
|
},
|
|
new Command("scanoops", "scanoops start end [ type ]", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 2 && t.countTokens() != 3) {
|
|
usage();
|
|
} else {
|
|
long stride = VM.getVM().getAddressSize();
|
|
Address base = VM.getVM().getDebugger().parseAddress(t.nextToken());
|
|
Address end = VM.getVM().getDebugger().parseAddress(t.nextToken());
|
|
Klass klass = null;
|
|
if (t.countTokens() == 1) {
|
|
klass = SystemDictionaryHelper.findInstanceKlass(t.nextToken());
|
|
if (klass == null) {
|
|
out.println("No such type.");
|
|
return;
|
|
}
|
|
}
|
|
while (base != null && base.lessThan(end)) {
|
|
long step = stride;
|
|
OopHandle handle = base.addOffsetToAsOopHandle(0);
|
|
if (RobustOopDeterminator.oopLooksValid(handle)) {
|
|
try {
|
|
Oop oop = VM.getVM().getObjectHeap().newOop(handle);
|
|
if (klass == null || oop.getKlass().isSubtypeOf(klass))
|
|
out.println(handle.toString() + " " + oop.getKlass().getName().asString());
|
|
step = oop.getObjectSize();
|
|
} catch (UnknownOopException ex) {
|
|
// ok
|
|
} catch (RuntimeException ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
base = base.addOffsetTo(step);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new Command("intConstant", "intConstant [ name [ value ] ]", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 2) {
|
|
usage();
|
|
return;
|
|
}
|
|
HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
|
|
if (t.countTokens() == 1) {
|
|
String name = t.nextToken();
|
|
out.println("intConstant " + name + " " + db.lookupIntConstant(name));
|
|
} else if (t.countTokens() == 0) {
|
|
Iterator i = db.getIntConstants();
|
|
while (i.hasNext()) {
|
|
String name = (String)i.next();
|
|
out.println("intConstant " + name + " " + db.lookupIntConstant(name));
|
|
}
|
|
} else if (t.countTokens() == 2) {
|
|
String name = t.nextToken();
|
|
int value = Integer.parseInt(t.nextToken());
|
|
db.addIntConstant(name, value);
|
|
}
|
|
}
|
|
},
|
|
new Command("longConstant", "longConstant [ name [ value ] ]", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 2) {
|
|
usage();
|
|
return;
|
|
}
|
|
HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
|
|
if (t.countTokens() == 1) {
|
|
String name = t.nextToken();
|
|
out.println("longConstant " + name + " " + db.lookupLongConstant(name));
|
|
} else if (t.countTokens() == 0) {
|
|
Iterator i = db.getLongConstants();
|
|
while (i.hasNext()) {
|
|
String name = (String)i.next();
|
|
out.println("longConstant " + name + " " + db.lookupLongConstant(name));
|
|
}
|
|
} else if (t.countTokens() == 2) {
|
|
String name = t.nextToken();
|
|
long value = Long.parseLong(t.nextToken());
|
|
db.addLongConstant(name, value);
|
|
}
|
|
}
|
|
},
|
|
new Command("field", "field [ type [ name fieldtype isStatic offset address ] ]", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) {
|
|
usage();
|
|
return;
|
|
}
|
|
if (t.countTokens() == 1) {
|
|
Type type = agent.getTypeDataBase().lookupType(t.nextToken());
|
|
dumpFields(type);
|
|
} else if (t.countTokens() == 0) {
|
|
Iterator i = agent.getTypeDataBase().getTypes();
|
|
while (i.hasNext()) {
|
|
dumpFields((Type)i.next());
|
|
}
|
|
} else {
|
|
BasicType containingType = (BasicType)agent.getTypeDataBase().lookupType(t.nextToken());
|
|
|
|
String fieldName = t.nextToken();
|
|
|
|
// The field's Type must already be in the database -- no exceptions
|
|
Type fieldType = agent.getTypeDataBase().lookupType(t.nextToken());
|
|
|
|
boolean isStatic = Boolean.parseBoolean(t.nextToken());
|
|
long offset = Long.parseLong(t.nextToken());
|
|
Address staticAddress = parseAddress(t.nextToken());
|
|
if (isStatic && staticAddress == null) {
|
|
staticAddress = lookup(containingType.getName() + "::" + fieldName);
|
|
}
|
|
|
|
// check to see if the field already exists
|
|
Iterator i = containingType.getFields();
|
|
while (i.hasNext()) {
|
|
Field f = (Field) i.next();
|
|
if (f.getName().equals(fieldName)) {
|
|
if (f.isStatic() != isStatic) {
|
|
throw new RuntimeException("static/nonstatic mismatch: " + t.input);
|
|
}
|
|
if (!isStatic) {
|
|
if (f.getOffset() != offset) {
|
|
throw new RuntimeException("bad redefinition of field offset: " + t.input);
|
|
}
|
|
} else {
|
|
if (!f.getStaticFieldAddress().equals(staticAddress)) {
|
|
throw new RuntimeException("bad redefinition of field location: " + t.input);
|
|
}
|
|
}
|
|
if (f.getType() != fieldType) {
|
|
throw new RuntimeException("bad redefinition of field type: " + t.input);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Create field by type
|
|
HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
|
|
db.createField(containingType,
|
|
fieldName, fieldType,
|
|
isStatic,
|
|
offset,
|
|
staticAddress);
|
|
|
|
}
|
|
}
|
|
|
|
},
|
|
new Command("tokenize", "tokenize ...", true) {
|
|
public void doit(Tokens t) {
|
|
while (t.hasMoreTokens()) {
|
|
out.println("\"" + t.nextToken() + "\"");
|
|
}
|
|
}
|
|
},
|
|
new Command("type", "type [ type [ name super isOop isInteger isUnsigned size ] ]", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1 && t.countTokens() != 0 && t.countTokens() != 6) {
|
|
usage();
|
|
return;
|
|
}
|
|
if (t.countTokens() == 6) {
|
|
String typeName = t.nextToken();
|
|
String superclassName = t.nextToken();
|
|
if (superclassName.equals("null")) {
|
|
superclassName = null;
|
|
}
|
|
boolean isOop = Boolean.parseBoolean(t.nextToken());
|
|
boolean isInteger = Boolean.parseBoolean(t.nextToken());
|
|
boolean isUnsigned = Boolean.parseBoolean(t.nextToken());
|
|
long size = Long.parseLong(t.nextToken());
|
|
|
|
BasicType type = null;
|
|
try {
|
|
type = (BasicType)agent.getTypeDataBase().lookupType(typeName);
|
|
} catch (RuntimeException e) {
|
|
}
|
|
if (type != null) {
|
|
if (type.isOopType() != isOop) {
|
|
throw new RuntimeException("oop mismatch in type definition: " + t.input);
|
|
}
|
|
if (type.isCIntegerType() != isInteger) {
|
|
throw new RuntimeException("integer type mismatch in type definition: " + t.input);
|
|
}
|
|
if (type.isCIntegerType() && (((CIntegerType)type).isUnsigned()) != isUnsigned) {
|
|
throw new RuntimeException("unsigned mismatch in type definition: " + t.input);
|
|
}
|
|
if (type.getSuperclass() == null) {
|
|
if (superclassName != null) {
|
|
if (type.getSize() == -1) {
|
|
type.setSuperclass(agent.getTypeDataBase().lookupType(superclassName));
|
|
} else {
|
|
throw new RuntimeException("unexpected superclass in type definition: " + t.input);
|
|
}
|
|
}
|
|
} else {
|
|
if (superclassName == null) {
|
|
throw new RuntimeException("missing superclass in type definition: " + t.input);
|
|
}
|
|
if (!type.getSuperclass().getName().equals(superclassName)) {
|
|
throw new RuntimeException("incorrect superclass in type definition: " + t.input);
|
|
}
|
|
}
|
|
if (type.getSize() != size) {
|
|
if (type.getSize() == -1) {
|
|
type.setSize(size);
|
|
}
|
|
throw new RuntimeException("size mismatch in type definition: " + t.input);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Create type
|
|
HotSpotTypeDataBase db = (HotSpotTypeDataBase)agent.getTypeDataBase();
|
|
db.createType(typeName, superclassName, isOop, isInteger, isUnsigned, size);
|
|
} else if (t.countTokens() == 1) {
|
|
Type type = agent.getTypeDataBase().lookupType(t.nextToken());
|
|
dumpType(type);
|
|
} else {
|
|
Iterator i = agent.getTypeDataBase().getTypes();
|
|
// Make sure the types are emitted in an order than can be read back in
|
|
HashSet<String> emitted = new HashSet<>();
|
|
ArrayDeque<Type> pending = new ArrayDeque<>();
|
|
while (i.hasNext()) {
|
|
Type n = (Type)i.next();
|
|
if (emitted.contains(n.getName())) {
|
|
continue;
|
|
}
|
|
|
|
while (n != null && !emitted.contains(n.getName())) {
|
|
pending.push(n);
|
|
n = n.getSuperclass();
|
|
}
|
|
while (!pending.isEmpty()) {
|
|
n = pending.pop();
|
|
dumpType(n);
|
|
emitted.add(n.getName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
},
|
|
new Command("source", "source filename", true) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
return;
|
|
}
|
|
String file = t.nextToken();
|
|
BufferedReader savedInput = in;
|
|
try {
|
|
BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
|
|
in = input;
|
|
run(false);
|
|
} catch (Exception e) {
|
|
out.println("Error: " + e);
|
|
if (verboseExceptions) {
|
|
e.printStackTrace(out);
|
|
}
|
|
} finally {
|
|
in = savedInput;
|
|
}
|
|
|
|
}
|
|
},
|
|
new Command("search", "search [ heap | perm | rawheap | codecache | threads ] value", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 2) {
|
|
usage();
|
|
return;
|
|
}
|
|
String type = t.nextToken();
|
|
final Address value = VM.getVM().getDebugger().parseAddress(t.nextToken());
|
|
final long stride = VM.getVM().getAddressSize();
|
|
if (type.equals("threads")) {
|
|
Threads threads = VM.getVM().getThreads();
|
|
for (int i = 0; i < threads.getNumberOfThreads(); i++) {
|
|
JavaThread thread = threads.getJavaThreadAt(i);
|
|
Address base = thread.getStackBase();
|
|
Address end = thread.getLastJavaSP();
|
|
if (end == null) continue;
|
|
if (end.lessThan(base)) {
|
|
Address tmp = base;
|
|
base = end;
|
|
end = tmp;
|
|
}
|
|
//out.println("Searching " + base + " " + end);
|
|
while (base != null && base.lessThan(end)) {
|
|
Address val = base.getAddressAt(0);
|
|
if (AddressOps.equal(val, value)) {
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
thread.printThreadIDOn(new PrintStream(bos));
|
|
out.println("found on the stack of thread " + bos.toString() + " at " + base);
|
|
}
|
|
base = base.addOffsetTo(stride);
|
|
}
|
|
}
|
|
} else if (type.equals("rawheap")) {
|
|
RawHeapVisitor iterator = new RawHeapVisitor() {
|
|
public void prologue(long used) {
|
|
}
|
|
|
|
public void visitAddress(Address addr) {
|
|
Address val = addr.getAddressAt(0);
|
|
if (AddressOps.equal(val, value)) {
|
|
out.println("found at " + addr);
|
|
}
|
|
}
|
|
public void visitCompOopAddress(Address addr) {
|
|
Address val = addr.getCompOopAddressAt(0);
|
|
if (AddressOps.equal(val, value)) {
|
|
out.println("found at " + addr);
|
|
}
|
|
}
|
|
public void epilogue() {
|
|
}
|
|
};
|
|
VM.getVM().getObjectHeap().iterateRaw(iterator);
|
|
} else if (type.equals("heap")) {
|
|
HeapVisitor iterator = new DefaultHeapVisitor() {
|
|
public boolean doObj(Oop obj) {
|
|
int index = 0;
|
|
Address start = obj.getHandle();
|
|
long end = obj.getObjectSize();
|
|
while (index < end) {
|
|
Address val = start.getAddressAt(index);
|
|
if (AddressOps.equal(val, value)) {
|
|
out.println("found in " + obj.getHandle());
|
|
break;
|
|
}
|
|
index += 4;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
VM.getVM().getObjectHeap().iterate(iterator);
|
|
} else if (type.equals("codecache")) {
|
|
CodeCacheVisitor v = new CodeCacheVisitor() {
|
|
public void prologue(Address start, Address end) {
|
|
}
|
|
public void visit(CodeBlob blob) {
|
|
boolean printed = false;
|
|
Address base = blob.getAddress();
|
|
Address end = base.addOffsetTo(blob.getSize());
|
|
while (base != null && base.lessThan(end)) {
|
|
Address val = base.getAddressAt(0);
|
|
if (AddressOps.equal(val, value)) {
|
|
if (!printed) {
|
|
printed = true;
|
|
try {
|
|
blob.printOn(out);
|
|
} catch (Exception e) {
|
|
out.println("Exception printing blob at " + base);
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
out.println("found at " + base + "\n");
|
|
}
|
|
base = base.addOffsetTo(stride);
|
|
}
|
|
}
|
|
public void epilogue() {
|
|
}
|
|
|
|
|
|
};
|
|
VM.getVM().getCodeCache().iterate(v);
|
|
|
|
}
|
|
}
|
|
},
|
|
new Command("dumpcodecache", "dumpcodecache", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 0) {
|
|
usage();
|
|
} else {
|
|
final PrintStream fout = out;
|
|
final HTMLGenerator gen = new HTMLGenerator(false);
|
|
CodeCacheVisitor v = new CodeCacheVisitor() {
|
|
public void prologue(Address start, Address end) {
|
|
}
|
|
public void visit(CodeBlob blob) {
|
|
fout.println(gen.genHTML(blob.contentBegin()));
|
|
}
|
|
public void epilogue() {
|
|
}
|
|
|
|
|
|
};
|
|
VM.getVM().getCodeCache().iterate(v);
|
|
}
|
|
}
|
|
},
|
|
new Command("where", "where { -a | id }", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
} else {
|
|
String name = t.nextToken();
|
|
Threads threads = VM.getVM().getThreads();
|
|
boolean all = name.equals("-a");
|
|
for (int i = 0; i < threads.getNumberOfThreads(); i++) {
|
|
JavaThread thread = threads.getJavaThreadAt(i);
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
thread.printThreadIDOn(new PrintStream(bos));
|
|
if (all || bos.toString().equals(name)) {
|
|
out.println("Thread " + bos.toString() + " Address: " + thread.getAddress());
|
|
HTMLGenerator gen = new HTMLGenerator(false);
|
|
try {
|
|
out.println(gen.genHTMLForJavaStackTrace(thread));
|
|
} catch (Exception e) {
|
|
err.println("Error: " + e);
|
|
if (verboseExceptions) {
|
|
e.printStackTrace(err);
|
|
}
|
|
}
|
|
if (!all) return;
|
|
}
|
|
}
|
|
if (!all) out.println("Couldn't find thread " + name);
|
|
}
|
|
}
|
|
},
|
|
new Command("threadcontext", "threadcontext [-v] { -a | id }", false) {
|
|
public void doit(Tokens t) {
|
|
boolean verbose = false;
|
|
if (t.countTokens() == 2) {
|
|
if (t.nextToken().equals("-v")) {
|
|
verbose = true;
|
|
} else {
|
|
usage();
|
|
return;
|
|
}
|
|
}
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
return;
|
|
}
|
|
String id = t.nextToken();
|
|
Threads threads = VM.getVM().getThreads();
|
|
boolean all = id.equals("-a");
|
|
for (int i = 0; i < threads.getNumberOfThreads(); i++) {
|
|
JavaThread thread = threads.getJavaThreadAt(i);
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
thread.printThreadIDOn(new PrintStream(bos));
|
|
if (all || bos.toString().equals(id)) {
|
|
out.format("Thread \"%s\" id=%s Address=%s\n",
|
|
thread.getThreadName(), bos.toString(), thread.getAddress());
|
|
thread.printThreadContextOn(out, verbose);
|
|
out.println(" ");
|
|
if (!all) return;
|
|
}
|
|
}
|
|
if (!all) {
|
|
out.println("Couldn't find thread " + id);
|
|
}
|
|
}
|
|
},
|
|
new Command("thread", "thread { -a | id }", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
} else {
|
|
String id = t.nextToken();
|
|
Threads threads = VM.getVM().getThreads();
|
|
boolean all = id.equals("-a");
|
|
for (int i = 0; i < threads.getNumberOfThreads(); i++) {
|
|
JavaThread thread = threads.getJavaThreadAt(i);
|
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
thread.printThreadIDOn(new PrintStream(bos));
|
|
if (all || bos.toString().equals(id)) {
|
|
out.println("Thread " + bos.toString() + " Address " + thread.getAddress());
|
|
thread.printInfoOn(out);
|
|
out.println(" ");
|
|
if (!all) return;
|
|
}
|
|
}
|
|
if (!all) {
|
|
out.println("Couldn't find thread " + id);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
new Command("threads", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 0) {
|
|
usage();
|
|
} else {
|
|
Threads threads = VM.getVM().getThreads();
|
|
for (int i = 0; i < threads.getNumberOfThreads(); i++) {
|
|
JavaThread thread = threads.getJavaThreadAt(i);
|
|
thread.printThreadIDOn(out);
|
|
out.println(" " + thread.getThreadName());
|
|
thread.printInfoOn(out);
|
|
out.println("\n...");
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
new Command("livenmethods", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 0) {
|
|
usage();
|
|
} else {
|
|
ArrayList<NMethod> nmethods = new ArrayList<>();
|
|
Threads threads = VM.getVM().getThreads();
|
|
HTMLGenerator gen = new HTMLGenerator(false);
|
|
for (int i = 0; i < threads.getNumberOfThreads(); i++) {
|
|
JavaThread thread = threads.getJavaThreadAt(i);
|
|
try {
|
|
for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
|
|
if (vf instanceof CompiledVFrame) {
|
|
NMethod c = ((CompiledVFrame)vf).getCode();
|
|
if (!nmethods.contains(c)) {
|
|
nmethods.add(c);
|
|
out.println(gen.genHTML(c));
|
|
}
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new Command("g1regiondetails", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 0) {
|
|
usage();
|
|
} else {
|
|
CollectedHeap heap = VM.getVM().getUniverse().heap();
|
|
if (!(heap instanceof G1CollectedHeap)) {
|
|
out.println("This command is valid only for G1GC.");
|
|
return;
|
|
}
|
|
out.println("Region Details:");
|
|
((G1CollectedHeap)heap).printRegionDetails(out);
|
|
}
|
|
}
|
|
},
|
|
new Command("universe", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 0) {
|
|
usage();
|
|
} else {
|
|
Universe u = VM.getVM().getUniverse();
|
|
out.println("Heap Parameters:");
|
|
u.heap().printOn(out);
|
|
}
|
|
}
|
|
},
|
|
new Command("verbose", "verbose true | false", true) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
} else {
|
|
verboseExceptions = Boolean.parseBoolean(t.nextToken());
|
|
}
|
|
}
|
|
},
|
|
new Command("assert", "assert true | false", true) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
} else {
|
|
Assert.ASSERTS_ENABLED = Boolean.parseBoolean(t.nextToken());
|
|
}
|
|
}
|
|
},
|
|
new Command("dumpclass", "dumpclass {address | name} [directory]", false) {
|
|
public void doit(Tokens t) {
|
|
int tokenCount = t.countTokens();
|
|
if (tokenCount != 1 && tokenCount != 2) {
|
|
usage();
|
|
return;
|
|
}
|
|
|
|
/* Find the InstanceKlass for specified class name or class address. */
|
|
InstanceKlass ik = null;
|
|
String classname = t.nextToken();
|
|
if (classname.startsWith("0x")) {
|
|
// treat it as address
|
|
VM vm = VM.getVM();
|
|
Address addr = vm.getDebugger().parseAddress(classname);
|
|
Metadata metadata = Metadata.instantiateWrapperFor(addr.addOffsetTo(0));
|
|
if (metadata instanceof InstanceKlass) {
|
|
ik = (InstanceKlass) metadata;
|
|
} else {
|
|
out.println("Specified address is not an InstanceKlass");
|
|
return;
|
|
}
|
|
} else {
|
|
ik = SystemDictionaryHelper.findInstanceKlass(classname);
|
|
if (ik == null) {
|
|
out.println("class not found: " + classname);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Compute filename for class. */
|
|
StringBuilder buf = new StringBuilder();
|
|
if (tokenCount > 1) {
|
|
buf.append(t.nextToken());
|
|
} else {
|
|
buf.append('.');
|
|
}
|
|
buf.append(File.separatorChar);
|
|
buf.append(ik.getName().asString().replace('/', File.separatorChar));
|
|
buf.append(".class");
|
|
String fileName = buf.toString();
|
|
File file = new File(fileName);
|
|
|
|
/* Dump the class file. */
|
|
try {
|
|
int index = fileName.lastIndexOf(File.separatorChar);
|
|
File dir = new File(fileName.substring(0, index));
|
|
dir.mkdirs();
|
|
try (FileOutputStream fos = new FileOutputStream(file)) {
|
|
ClassWriter cw = new ClassWriter(ik, fos);
|
|
cw.write();
|
|
}
|
|
} catch (Exception e) {
|
|
err.println("Error: " + e);
|
|
if (verboseExceptions) {
|
|
e.printStackTrace(err);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new Command("sysprops", "sysprops", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 0) {
|
|
usage();
|
|
return;
|
|
}
|
|
SysPropsDumper sysProps = new SysPropsDumper();
|
|
sysProps.run();
|
|
}
|
|
},
|
|
new Command("dumpheap", "dumpheap [gz=<1-9>] [filename]", false) {
|
|
public void doit(Tokens t) {
|
|
int cntTokens = t.countTokens();
|
|
if (cntTokens > 2) {
|
|
err.println("More than 2 options specified: " + cntTokens);
|
|
usage();
|
|
return;
|
|
}
|
|
JMap jmap = new JMap();
|
|
String filename = "heap.bin";
|
|
int gzlevel = 0;
|
|
/*
|
|
* Possible command:
|
|
* dumpheap gz=1 file;
|
|
* dumpheap gz=1;
|
|
* dumpheap file;
|
|
* dumpheap
|
|
*
|
|
* Use default filename if cntTokens == 0.
|
|
* Handle cases with cntTokens == 1 or 2.
|
|
*/
|
|
if (cntTokens == 1) { // first argument could be filename or "gz="
|
|
String option = t.nextToken();
|
|
if (!option.startsWith("gz=")) {
|
|
filename = option;
|
|
} else {
|
|
gzlevel = parseHeapDumpCompressionLevel(option);
|
|
if (gzlevel == 0) {
|
|
usage();
|
|
return;
|
|
}
|
|
filename = "heap.bin.gz";
|
|
}
|
|
}
|
|
if (cntTokens == 2) { // first argument is "gz=" followed by filename
|
|
String option = t.nextToken();
|
|
gzlevel = parseHeapDumpCompressionLevel(option);
|
|
if (gzlevel == 0) {
|
|
usage();
|
|
return;
|
|
}
|
|
filename = t.nextToken();
|
|
if (filename.startsWith("gz=")) {
|
|
err.println("Filename should not start with \"gz=\": " + filename);
|
|
usage();
|
|
return;
|
|
}
|
|
}
|
|
try {
|
|
jmap.writeHeapHprofBin(filename, gzlevel);
|
|
} catch (Exception e) {
|
|
err.println("Error: " + e);
|
|
if (verboseExceptions) {
|
|
e.printStackTrace(err);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new Command("class", "class name", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 1) {
|
|
usage();
|
|
return;
|
|
}
|
|
String classname = t.nextToken();
|
|
InstanceKlass ik = SystemDictionaryHelper.findInstanceKlass(classname);
|
|
if (ik == null) {
|
|
out.println("class not found: " + classname);
|
|
} else {
|
|
out.println(ik.getName().asString() + " @" + ik.getAddress());
|
|
}
|
|
}
|
|
},
|
|
new Command("classes", "classes", false) {
|
|
public void doit(Tokens t) {
|
|
if (t.countTokens() != 0) {
|
|
usage();
|
|
return;
|
|
}
|
|
ClassLoaderDataGraph cldg = VM.getVM().getClassLoaderDataGraph();
|
|
cldg.classesDo(new ClassLoaderDataGraph.ClassVisitor() {
|
|
public void visit(Klass k) {
|
|
out.println(k.getName().asString() + " @" + k.getAddress());
|
|
}
|
|
}
|
|
);
|
|
}
|
|
},
|
|
};
|
|
|
|
private boolean verboseExceptions = false;
|
|
private ArrayList<String> history = new ArrayList<>();
|
|
private HashMap<String, Command> commands = new HashMap<>();
|
|
private boolean doEcho = false;
|
|
|
|
private Command findCommand(String key) {
|
|
return commands.get(key);
|
|
}
|
|
|
|
public void printPrompt() {
|
|
out.print("hsdb> ");
|
|
}
|
|
|
|
private DebuggerInterface debugger;
|
|
private HotSpotAgent agent;
|
|
private BufferedReader in;
|
|
private PrintStream out;
|
|
private PrintStream err;
|
|
|
|
// called before debuggee attach
|
|
private void preAttach() {
|
|
// nothing for now..
|
|
}
|
|
|
|
// called after debuggee attach
|
|
private void postAttach() {
|
|
// nothing for now..
|
|
}
|
|
|
|
public void setOutput(PrintStream o) {
|
|
out = o;
|
|
}
|
|
|
|
public void setErr(PrintStream e) {
|
|
err = e;
|
|
}
|
|
|
|
public CommandProcessor(DebuggerInterface debugger, BufferedReader in, PrintStream out, PrintStream err) {
|
|
this.debugger = debugger;
|
|
this.agent = debugger.getAgent();
|
|
this.in = in;
|
|
this.out = out;
|
|
this.err = err;
|
|
for (int i = 0; i < commandList.length; i++) {
|
|
Command c = commandList[i];
|
|
if (commands.get(c.name) != null) {
|
|
throw new InternalError(c.name + " has multiple definitions");
|
|
}
|
|
commands.put(c.name, c);
|
|
}
|
|
if (debugger.isAttached()) {
|
|
postAttach();
|
|
}
|
|
}
|
|
|
|
|
|
public void run(boolean prompt) {
|
|
// Process interactive commands.
|
|
while (!quit) {
|
|
if (prompt) printPrompt();
|
|
String ln = null;
|
|
try {
|
|
ln = in.readLine();
|
|
} catch (IOException e) {
|
|
}
|
|
if (ln == null) {
|
|
if (prompt) err.println("Input stream closed.");
|
|
return;
|
|
}
|
|
|
|
executeCommand(ln, prompt);
|
|
}
|
|
}
|
|
|
|
static Pattern historyPattern = Pattern.compile("([\\\\]?)((!\\*)|(!\\$)|(!!-?)|(!-?[0-9][0-9]*)|(![a-zA-Z][^ ]*))");
|
|
|
|
public void executeCommand(String ln, boolean putInHistory) {
|
|
if (ln.indexOf('!') != -1) {
|
|
int size = history.size();
|
|
if (size == 0) {
|
|
ln = "";
|
|
err.println("History is empty");
|
|
} else {
|
|
StringBuilder result = new StringBuilder();
|
|
Matcher m = historyPattern.matcher(ln);
|
|
int start = 0;
|
|
while (m.find()) {
|
|
// Capture the text preceding the matched text.
|
|
if (m.start() > start) {
|
|
result.append(ln.substring(start, m.start()));
|
|
}
|
|
start = m.end();
|
|
|
|
if (m.group(1).length() != 0) {
|
|
// This means we matched a `\` before the '!'. Don't do any history
|
|
// expansion in this case. Just capture what matched after the `\`.
|
|
result.append(m.group(2));
|
|
continue;
|
|
}
|
|
|
|
String cmd = m.group(2);
|
|
if (cmd.equals("!!")) {
|
|
result.append(history.get(history.size() - 1));
|
|
} else if (cmd.equals("!!-")) {
|
|
Tokens item = new Tokens(history.get(history.size() - 1));
|
|
item.trim(1);
|
|
result.append(item.join(" "));
|
|
} else if (cmd.equals("!*")) {
|
|
Tokens item = new Tokens(history.get(history.size() - 1));
|
|
item.nextToken();
|
|
result.append(item.join(" "));
|
|
} else if (cmd.equals("!$")) {
|
|
Tokens item = new Tokens(history.get(history.size() - 1));
|
|
result.append(item.at(item.countTokens() - 1));
|
|
} else {
|
|
String tail = cmd.substring(1);
|
|
switch (tail.charAt(0)) {
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
case '-': {
|
|
int index = Integer.parseInt(tail);
|
|
if (index < 0) {
|
|
index = history.size() + index;
|
|
}
|
|
if (index > size) {
|
|
err.println("No such history item");
|
|
} else {
|
|
result.append(history.get(index));
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
for (int i = history.size() - 1; i >= 0; i--) {
|
|
String s = history.get(i);
|
|
if (s.startsWith(tail)) {
|
|
result.append(s);
|
|
break; // only capture the most recent match in the history
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (result.length() == 0) {
|
|
err.println("malformed history reference");
|
|
ln = "";
|
|
} else {
|
|
if (start < ln.length()) {
|
|
result.append(ln.substring(start));
|
|
}
|
|
ln = result.toString();
|
|
if (!doEcho) {
|
|
out.println(ln);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (doEcho) {
|
|
out.println("+ " + ln);
|
|
}
|
|
|
|
PrintStream redirect = null;
|
|
Tokens t = new Tokens(ln);
|
|
if (t.hasMoreTokens()) {
|
|
boolean error = false;
|
|
if (putInHistory) history.add(ln);
|
|
int len = t.countTokens();
|
|
if (len > 2) {
|
|
String r = t.at(len - 2);
|
|
if (r.equals(">") || r.equals(">>")) {
|
|
boolean append = r.length() == 2;
|
|
String file = t.at(len - 1);
|
|
try {
|
|
redirect = new PrintStream(new BufferedOutputStream(new FileOutputStream(file, append)));
|
|
t.trim(2);
|
|
} catch (Exception e) {
|
|
out.println("Error: " + e);
|
|
if (verboseExceptions) {
|
|
e.printStackTrace(out);
|
|
}
|
|
error = true;
|
|
}
|
|
}
|
|
}
|
|
if (!error) {
|
|
PrintStream savedout = out;
|
|
if (redirect != null) {
|
|
out = redirect;
|
|
}
|
|
try {
|
|
executeCommand(t);
|
|
} catch (Exception e) {
|
|
err.println("Error: " + e);
|
|
if (verboseExceptions) {
|
|
e.printStackTrace(err);
|
|
}
|
|
} finally {
|
|
if (redirect != null) {
|
|
out = savedout;
|
|
redirect.close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void executeCommand(Tokens args) {
|
|
String cmd = args.nextToken();
|
|
|
|
Command doit = findCommand(cmd);
|
|
|
|
/*
|
|
* Check for an unknown command
|
|
*/
|
|
if (doit == null) {
|
|
out.println("Unrecognized command. Try help...");
|
|
} else if (!debugger.isAttached() && !doit.okIfDisconnected) {
|
|
out.println("Command not valid until attached to a VM");
|
|
} else {
|
|
try {
|
|
doit.doit(args);
|
|
} catch (Exception e) {
|
|
out.println("Error: " + e);
|
|
if (verboseExceptions) {
|
|
e.printStackTrace(out);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Parse compression level
|
|
* @return 1-9 compression level
|
|
* 0 compression level is illegal
|
|
*/
|
|
private int parseHeapDumpCompressionLevel(String option) {
|
|
|
|
String[] keyValue = option.split("=");
|
|
if (!keyValue[0].equals("gz")) {
|
|
err.println("Expected option is \"gz=\"");
|
|
return 0;
|
|
}
|
|
if (keyValue.length != 2) {
|
|
err.println("Exactly one argument is expected for option \"gz\"");
|
|
return 0;
|
|
}
|
|
int gzl = 0;
|
|
String level = keyValue[1];
|
|
try {
|
|
gzl = Integer.parseInt(level);
|
|
} catch (NumberFormatException e) {
|
|
err.println("gz option value not an integer ("+level+")");
|
|
return 0;
|
|
}
|
|
if (gzl < 1 || gzl > 9) {
|
|
err.println("Compression level out of range (1-9): " + level);
|
|
return 0;
|
|
}
|
|
return gzl;
|
|
}
|
|
}
|