/*
* Copyright (c) 2007, 2012, 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 com.sun.max.vm;
import static com.sun.max.vm.MaxineVM.*;
import static com.sun.max.vm.intrinsics.MaxineIntrinsicIDs.*;
import static com.sun.max.vm.thread.VmThreadLocal.*;
import java.io.*;
import java.util.*;
import com.sun.max.annotate.*;
import com.sun.max.lang.*;
import com.sun.max.program.*;
import com.sun.max.program.ProgramWarning.Handler;
import com.sun.max.unsafe.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.compiler.target.*;
import com.sun.max.vm.heap.gcx.*;
import com.sun.max.vm.layout.*;
import com.sun.max.vm.object.*;
import com.sun.max.vm.reference.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.thread.*;
/**
* This class presents a low-level VM logging facility that closely resembles (but extends) that offered by standard
* {@link PrintStream}s. All output of the methods in this class goes to a configurable {@linkplain #os output stream}.
* The environment variable MAXINE_LOG_FILE can be used to specify a file as the log output stream.
*/
@NEVER_INLINE
public final class Log {
static final int CHAR_ARRAY_BASE_OFFSET = Layout.charArrayLayout().getElementOffsetFromOrigin(0).toInt();
static final int BYTE_ARRAY_BASE_OFFSET = Layout.byteArrayLayout().getElementOffsetFromOrigin(0).toInt();
private Log() {
}
/**
* @param len if negative, then print until a terminating 0 is encountered
*/
@C_FUNCTION
static native void log_print_bytes(Address jbytes, int offset, int len);
@C_FUNCTION
static native void log_print_chars(Address jchars, int offset, int len);
@C_FUNCTION
static native void log_print_boolean(boolean val);
@C_FUNCTION
static native void log_print_char(int val);
@C_FUNCTION
static native void log_print_int(int val);
@C_FUNCTION
static native void log_print_long(long val);
@C_FUNCTION
static native void log_print_float(float val);
@C_FUNCTION
static native void log_print_double(double val);
@C_FUNCTION
static native void log_print_word(Word val);
@C_FUNCTION
static native void log_print_newline();
@C_FUNCTION
static native void log_print_symbol(Address address);
@C_FUNCTION
static native void log_lock();
@C_FUNCTION
static native void log_unlock();
@C_FUNCTION
static native void log_flush();
/**
* The singleton VM log output stream.
*
* This output stream writes to the native standard output stream by default. It can be redirected
* to write to the native standard error stream or a file instead by setting the value of the
* environment variable (<b>not</b> system property) named {@code MAXINE_LOG_FILE}.
* If set, the value of this environment variable is interpreted as a file path to which VM
* output will be written. The special values {@code "stdout"} and {@code "stderr"} are
* interpreted to mean the native standard output and error streams respectively.
*/
public static final OutputStream os = new LogOutputStream();
/**
* The singleton VM print stream. This print stream sends all its output to {@link #os}.
*/
public static final LogPrintStream out = new LogPrintStream(os);
/**
* Equivalent to calling {@link LogPrintStream#print(String)} on {@link #out}.
*/
public static void print(String s) {
out.print(s);
}
/**
* Equivalent to calling {@link LogPrintStream#printSymbol(Word)} on {@link #out}.
*/
public static void printSymbol(Word address) {
out.printSymbol(address);
}
public static void printCString(Pointer cString) {
out.printCString(cString);
}
/**
* Equivalent to calling {@link LogPrintStream#printSymbol(CodePointer)} on {@link #out}.
*/
public static void printSymbol(CodePointer cp) {
out.printSymbol(cp);
}
/**
* Equivalent to calling {@link com.sun.max.vm.Log.LogPrintStream#printf(String, Object[])} on {@link #out}.
*/
public static void print(String... arr) {
for (String s : arr) {
out.print(s);
}
}
/**
* Equivalent to calling {@link LogPrintStream#print(String, boolean)} on {@link #out}.
*/
public static void print(String s, boolean withNewLine) {
out.print(s, withNewLine);
}
/**
* Equivalent to calling {@link LogPrintStream#print(String, int)} on {@link #out}.
*/
public static void print(String s, int width) {
out.print(s, width);
}
/**
* Equivalent to calling {@link LogPrintStream#print(Object)} on {@link #out}.
*/
public static void print(Object object) {
out.print(object);
}
public static void flush() {
out.flush();
}
/**
* Equivalent to calling {@link LogPrintStream#print(int)} on {@link #out}.
*/
public static void print(int i) {
out.print(i);
}
/**
* Equivalent to calling {@link LogPrintStream#print(long)} on {@link #out}.
*/
public static void print(long i) {
out.print(i);
}
/**
* Equivalent to calling {@link LogPrintStream#print(char)} on {@link #out}.
*/
public static void print(char c) {
out.print(c);
}
/**
* Equivalent to calling {@link LogPrintStream#print(boolean)} on {@link #out}.
*/
public static void print(boolean b) {
out.print(b);
}
/**
* Equivalent to calling {@link LogPrintStream#print(double)} on {@link #out}.
*/
public static void print(double d) {
out.print(d);
}
/**
* Equivalent to calling {@link LogPrintStream#print(float)} on {@link #out}.
*/
public static void print(float f) {
out.print(f);
}
/**
* Equivalent to calling {@link LogPrintStream#print(char[])} on {@link #out}.
*/
public static void print(char[] c) {
out.print(c);
}
/**
* Equivalent to calling {@link LogPrintStream#print(Word)} on {@link #out}.
*/
public static void print(Word word) {
out.print(word);
}
public static void print(RegionRange regionRange) {
final int first = regionRange.firstRegion();
final int last = first + regionRange.numRegions() - 1;
out.print("[");
out.print(first);
out.print(",");
out.print(last);
out.print("]");
}
/**
* Print an address range.
* @param start first address in the range
* @param end last address in the range
*/
public static void printRange(Word start, Word end, boolean withNewLine) {
out.print("[");
out.print(start);
out.print(",");
out.print(end);
if (withNewLine) {
out.println("]");
} else {
out.print("]");
}
}
/**
* Equivalent to calling {@link LogPrintStream#print(CodePointer)} on {@link #out}.
*/
public static void print(CodePointer cp) {
out.print(cp);
}
/**
* Equivalent to calling {@link LogPrintStream#println(String)} on {@link #out}.
*/
public static void println(String s) {
out.println(s);
}
/**
* Equivalent to calling {@link LogPrintStream#print(String, int)} on {@link #out}.
*/
public static void println(String s, int width) {
out.println(s, width);
}
/**
* Equivalent to calling {@link LogPrintStream#println(Object)} on {@link #out}.
*/
public static void println(Object object) {
out.println(object);
}
/**
* Equivalent to calling {@link LogPrintStream#println()} on {@link #out}.
*/
public static void println() {
out.println();
}
/**
* Equivalent to calling {@link LogPrintStream#println(int)} on {@link #out}.
*/
public static void println(int i) {
out.println(i);
}
/**
* Equivalent to calling {@link LogPrintStream#println(long)} on {@link #out}.
*/
public static void println(long i) {
out.println(i);
}
/**
* Equivalent to calling {@link LogPrintStream#println(char)} on {@link #out}.
*/
public static void println(char c) {
out.println(c);
}
/**
* Equivalent to calling {@link LogPrintStream#println(boolean)} on {@link #out}.
*/
public static void println(boolean b) {
out.println(b);
}
/**
* Equivalent to calling {@link LogPrintStream#println(double)} on {@link #out}.
*/
public static void println(double d) {
out.println(d);
}
/**
* Equivalent to calling {@link LogPrintStream#println(float)} on {@link #out}.
*/
public static void println(float f) {
out.println(f);
}
/**
* Equivalent to calling {@link LogPrintStream#println(char[])} on {@link #out}.
*/
public static void println(char[] c) {
out.println(c);
}
/**
* Equivalent to calling {@link LogPrintStream#println(Word)} on {@link #out}.
*/
public static void println(Word word) {
out.println(word);
}
public static void println(RegionRange regionRange) {
print(regionRange);
out.println();
}
/**
* Equivalent to calling (@link LogPrintStream#println(CodePointer)} on {@link #out}.
*/
public static void println(CodePointer cp) {
out.println(cp);
}
/**
* Equivalent to calling {@link LogPrintStream#printField(FieldActor, boolean)} on {@link #out}.
*/
public static void printFieldActor(FieldActor fieldActor, boolean withNewline) {
out.printField(fieldActor, withNewline);
}
/**
* Equivalent to calling {@link Log#printMethod(TargetMethod, boolean)} passing {@code false}
* as the {@code boolean} parameter.
*/
public static void print(TargetMethod tm) {
printMethod(tm, false);
}
/**
* Equivalent to calling {@link Log#printMethod(TargetMethod, boolean)} passing {@code true}
* as the {@code boolean} parameter.
*/
public static void println(TargetMethod tm) {
printMethod(tm, true);
}
/**
* Equivalent to calling {@link Log#printMethod(MethodActor, boolean)} passing {@code false}
* as the {@code boolean} parameter.
*/
public static void print(MethodActor ma) {
printMethod(ma, false);
}
/**
* Equivalent to calling {@link Log#printMethod(MethodActor, boolean)} passing {@code true}
* as the {@code boolean} parameter.
*/
public static void println(MethodActor ma) {
printMethod(ma, true);
}
/**
* Equivalent to calling {@link Log#printHub(Hub, boolean)} passing {@code false} as the
* {@code boolean} parameter.
*/
public static void print(Hub hub) {
printHub(hub, false);
}
/**
* Equivalent to calling {@link Log#printHub(Hub, boolean)} passing {@code true} as the
* {@code boolean} parameter.
*/
public static void println(Hub hub) {
printHub(hub, true);
}
/**
* Equivalent to calling {@link LogPrintStream#printMethod(MethodActor, boolean)} on {@link #out}.
*/
public static void printMethod(MethodActor methodActor, boolean withNewline) {
if (methodActor == null) {
out.print("<no method actor>");
if (withNewline) {
out.println();
}
} else {
out.printMethod(methodActor, withNewline);
}
}
/**
* Equivalent to calling {@link LogPrintStream#printMethod(TargetMethod, boolean)} on {@link #out}.
*/
public static void printMethod(TargetMethod targetMethod, boolean withNewline) {
if (targetMethod == null) {
out.print("<no target method>");
if (withNewline) {
out.println();
}
} else {
out.printMethod(targetMethod, withNewline);
}
}
/**
* Equivalent to calling {@link LogPrintStream#printLocation(TargetMethod, int, boolean)} on {@link #out}.
*/
public static void printLocation(TargetMethod tm, int pos, boolean withNewline) {
out.printLocation(tm, pos, withNewline);
}
/**
* Equivalent to calling {@link #printLocation(TargetMethod, int, boolean) printLocation}{@code (tm, tm.posFor(ip), withNewLine)}.
*/
public static void printLocation(TargetMethod tm, CodePointer ip, boolean withNewline) {
printLocation(tm, tm.posFor(ip), withNewline);
}
/**
* Equivalent to calling {@link LogPrintStream#printHub(Hub, boolean)} on {@link #out}.
*/
public static void printHub(Hub hub, boolean withNewLine) {
if (hub == null) {
out.print("<no hub>");
if (withNewLine) {
out.println();
}
} else {
out.printHub(hub, withNewLine);
}
}
/**
* Prints the current VM thread via a call to {@link LogPrintStream#printThread(VmThread, boolean)} on {@link #out}.
*/
public static void printCurrentThread(boolean withNewline) {
out.printThread(VmThread.current(), withNewline);
}
/**
* Equivalent to calling {@link LogPrintStream#printThread(VmThread, boolean)} on {@link #out}.
*/
public static void printThread(VmThread vmThread, boolean withNewline) {
out.printThread(vmThread, withNewline);
}
/**
* Equivalent to calling {@link LogPrintStream#printThreadLocals(Pointer, boolean)} on {@link #out}.
*/
public static void printThreadLocals(Pointer tla, boolean all) {
out.printThreadLocals(tla, all);
}
/**
* Equivalent to a sequence of call to{@link LogPrintStream#print(long)} and {@link LogPrintStream#print(char)} on {@link #out} with, respectively, the
* size converted to the unit and the char representing the unit.
*/
public static void printToPowerOfTwoUnits(Size size) {
out.printToPowerOfTwoUnits(size);
}
/**
* Equivalent to a sequence of call to{@link LogPrintStream#print(long)} and {@link LogPrintStream#println(char)} on {@link #out} with, respectively, the
* size converted to the unit and the char representing the unit.
*/
public static void printlnToPowerOfTwoUnits(Size size) {
out.printlnToPowerOfTwoUnits(size);
}
private static final class LogOutputStream extends OutputStream {
/**
* Only a {@linkplain Log#os singleton} instance of this class exists.
*/
private LogOutputStream() {
}
@HOSTED_ONLY
private static final OutputStream hostedOutputStream;
static {
// Use the same environment variable as used by the native code - see Native/share/debug.c
String path = System.getenv("MAXINE_LOG_FILE");
if (path == null) {
path = "stdout";
}
if (path.equals("stdout")) {
hostedOutputStream = System.out;
} else if (path.equals("stderr")) {
hostedOutputStream = System.err;
} else {
try {
hostedOutputStream = new FileOutputStream(path);
} catch (FileNotFoundException fileNotFoundException) {
throw ProgramError.unexpected("Could not open file for VM output stream: " + path, fileNotFoundException);
}
}
}
@Override
public void write(int b) throws IOException {
if (MaxineVM.isHosted()) {
hostedOutputStream.write(b);
if (b == '\n') {
hostedOutputStream.flush();
}
} else {
log_print_char(b);
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (MaxineVM.isHosted()) {
hostedOutputStream.write(b, off, len);
for (int i = (off + len) - 1; i >= off; --i) {
if (b[i] == '\n') {
hostedOutputStream.flush();
break;
}
}
} else {
log_print_bytes(Reference.fromJava(b).toOrigin().plus(BYTE_ARRAY_BASE_OFFSET), off, len);
}
}
}
public static final class LogPrintStream extends PrintStream {
/**
* Prints a given char array to the log stream. The log {@linkplain Log#lock() lock}
* must be held by the caller.
*/
private void printChars(char[] ch) {
log_print_chars(Reference.fromJava(ch).toOrigin().plus(CHAR_ARRAY_BASE_OFFSET), 0, ch.length);
}
/**
* Prints symbolic information available for a given address (if any). This method
* uses the dladdr(3) function on Unix platforms. Typical output for a call to this
* method may be:
* <pre>
*
* </pre>
*
* If no symbolic information is available for {@code address}, then this method is
* equivalent to calling {@link #print(Word)} with the value of {@code address}.
*
* @param address
*/
public void printSymbol(Word address) {
if (MaxineVM.isHosted()) {
super.print(address.toHexString());
} else {
log_print_symbol(address.asAddress());
}
}
public void printSymbol(CodePointer cp) {
printSymbol(cp.toAddress());
}
/**
* Print a C string.
* @param cString
*/
public void printCString(Pointer cString) {
log_print_bytes(cString, 0, -1);
}
static class StringAlias {
@ALIAS(declaringClass = String.class)
char[] value;
@ALIAS(declaringClass = String.class, optional = true)
int offset;
@ALIAS(declaringClass = String.class, optional = true)
int count;
}
@INTRINSIC(UNSAFE_CAST)
public static native StringAlias asStringAlias(String s);
/**
* Starting with JDK 7 update 6, the String class no longer has the offset and count fields.
* We want to support both String variants (this is necessary until we drop support for JDK 6).
*/
@FOLD
private static boolean stringHasOffset() {
try {
String.class.getDeclaredField("offset");
} catch (NoSuchFieldException e) {
return false;
}
return true;
}
/**
* Prints a given string to the log stream. The log {@linkplain Log#lock() lock}
* must be held by the caller.
*/
private void printString(String string) {
final StringAlias s = asStringAlias(string == null ? "null" : string);
if (stringHasOffset()) {
log_print_chars(Reference.fromJava(s.value).toOrigin().plus(CHAR_ARRAY_BASE_OFFSET), s.offset, s.count);
} else {
log_print_chars(Reference.fromJava(s.value).toOrigin().plus(CHAR_ARRAY_BASE_OFFSET), 0, s.value.length);
}
}
/**
* Only a {@linkplain Log#out singleton} instance of this class exists.
*/
private LogPrintStream(OutputStream output) {
super(output);
}
@Override
public void flush() {
if (MaxineVM.isHosted()) {
super.flush();
} else {
log_flush();
}
}
public void print(String s, boolean withNewline) {
if (withNewline) {
println(s);
} else {
print(s);
}
}
/**
* Prints a given string to this stream, padded by zero or more spaces.
*
* @param s the string to print
* @param width the minimum number of characters to be printed, {@code w}, is the absolute value of {@code
* width}. The number of padding spaces printed is {@code s.length() - w}. If {@code width} is
* negative, then the padding spaces are printed before {@code s} otherwise they are printed after
* {@code s}.
*/
public void print(String s, int width) {
if (width < 0) {
int padding = s.length() + width;
while (padding-- >= 0) {
print(' ');
}
}
print(s);
if (width > 0) {
int padding = s.length() - width;
while (padding-- >= 0) {
print(' ');
}
}
}
/**
* Convenience method for a call to {@link #print(String, int)} followed by {@link #println()}.
*/
public void println(String s, int width) {
print(s, width);
println();
}
@Override
public void print(String s) {
if (MaxineVM.isHosted()) {
super.print(s);
} else {
final boolean lockDisabledSafepoints = Log.lock();
printString(s);
Log.unlock(lockDisabledSafepoints);
}
}
@Override
public void print(Object object) {
if (MaxineVM.isHosted()) {
super.print(object);
} else {
final String string = String.valueOf(object);
final boolean lockDisabledSafepoints = Log.lock();
printString(string);
Log.unlock(lockDisabledSafepoints);
}
}
@Override
public void print(int i) {
if (MaxineVM.isHosted()) {
super.print(i);
} else {
// locking is not really necessary for primitives
log_print_int(i);
}
}
@Override
public void print(long i) {
if (MaxineVM.isHosted()) {
super.print(i);
} else {
// locking is not really necessary for primitives
log_print_long(i);
}
}
@Override
public void print(char c) {
if (MaxineVM.isHosted()) {
super.print(c);
} else {
// locking is not really necessary for primitives
log_print_char(c);
}
}
@Override
public void print(boolean b) {
if (MaxineVM.isHosted()) {
super.print(b);
} else {
// locking is not really necessary for primitives
log_print_boolean(b);
}
}
@Override
public void print(double d) {
if (MaxineVM.isHosted()) {
super.print(d);
} else {
// locking is not really necessary for primitives
log_print_double(d);
}
}
@Override
public void print(float f) {
if (MaxineVM.isHosted()) {
super.print(f);
} else {
// locking is not really necessary for primitives
log_print_float(f);
}
}
@Override
public void print(char[] c) {
if (MaxineVM.isHosted()) {
super.print(c);
} else {
final boolean lockDisabledSafepoints = Log.lock();
printChars(c);
Log.unlock(lockDisabledSafepoints);
}
}
public void print(Word word) {
if (MaxineVM.isHosted()) {
super.print(word.toHexString());
} else {
// locking is not really necessary for primitives
log_print_word(word);
}
}
public void print(CodePointer cp) {
if (MaxineVM.isHosted()) {
print(cp.toAddress());
} else {
// locking is not really necessary for primitives
log_print_word(cp.toAddress());
}
}
@Override
public void println(String s) {
if (MaxineVM.isHosted()) {
super.println(s);
} else {
final boolean lockDisabledSafepoints = Log.lock();
printString(s);
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
@Override
public void println() {
if (MaxineVM.isHosted()) {
super.println();
} else {
final boolean lockDisabledSafepoints = Log.lock();
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
@Override
public void println(int i) {
if (MaxineVM.isHosted()) {
super.println(i);
} else {
final boolean lockDisabledSafepoints = Log.lock();
log_print_int(i);
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
@Override
public void println(long i) {
if (MaxineVM.isHosted()) {
super.println(i);
} else {
final boolean lockDisabledSafepoints = Log.lock();
log_print_long(i);
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
@Override
public void println(char c) {
if (MaxineVM.isHosted()) {
super.println(c);
} else {
final boolean lockDisabledSafepoints = Log.lock();
log_print_char(c);
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
@Override
public void println(boolean b) {
if (MaxineVM.isHosted()) {
super.println(b);
} else {
final boolean lockDisabledSafepoints = Log.lock();
log_print_boolean(b);
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
@Override
public void println(double d) {
if (MaxineVM.isHosted()) {
super.println(d);
} else {
final boolean lockDisabledSafepoints = Log.lock();
log_print_double(d);
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
@Override
public void println(float f) {
if (MaxineVM.isHosted()) {
super.println(f);
} else {
final boolean lockDisabledSafepoints = Log.lock();
log_print_float(f);
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
@Override
public void println(char[] c) {
if (MaxineVM.isHosted()) {
super.println(c);
} else {
final boolean lockDisabledSafepoints = Log.lock();
printChars(c);
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
@Override
public void println(Object object) {
if (MaxineVM.isHosted()) {
super.println(object);
} else {
final String string = String.valueOf(object);
final boolean lockDisabledSafepoints = Log.lock();
printString(string);
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
public void println(Word word) {
if (MaxineVM.isHosted()) {
super.println(word.toHexString());
} else {
final boolean lockDisabledSafepoints = Log.lock();
log_print_word(word);
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
public void println(CodePointer cp) {
if (MaxineVM.isHosted()) {
println(cp.toAddress());
} else {
final boolean lockDisabledSafepoints = Log.lock();
log_print_word(cp.toAddress());
log_print_newline();
Log.unlock(lockDisabledSafepoints);
}
}
/**
* Prints a {@link FieldActor} to this stream. The output is of the form:
*
* <pre>
* <holder>.<name>:<descriptor>
* </pre>
*
* For example, the output for {@link System#err} is:
*
* <pre>
* "java.lang.System.err:Ljava/io/PrintStream;"
* </pre>
* @param fieldActor the field actor to print
* @param withNewline specifies if a newline should be appended to the stream after the field actor
*/
public void printField(FieldActor fieldActor, boolean withNewline) {
boolean lockDisabledSafepoints = false;
if (!MaxineVM.isHosted()) {
lockDisabledSafepoints = lock();
}
print(fieldActor.holder().name.string);
print('.');
print(fieldActor.name.string);
print(":");
print(fieldActor.descriptor().string, withNewline);
if (!MaxineVM.isHosted()) {
unlock(lockDisabledSafepoints);
}
}
/**
* Prints a {@link MethodActor} to this stream. The output is of the form:
*
* <pre>
* <holder>.<name><descriptor>
* </pre>
*
* For example, the output for {@link Runnable#run()} is:
*
* <pre>
* "java.lang.Runnable.run()V"
* </pre>
* @param methodActor the method actor to print
* @param withNewline specifies if a newline should be appended to the stream after the method actor
*/
public void printMethod(MethodActor methodActor, boolean withNewline) {
boolean lockDisabledSafepoints = lock();
print(methodActor.holder().name.string);
print('.');
print(methodActor.name.string);
print(methodActor.descriptor().string, withNewline);
unlock(lockDisabledSafepoints);
}
/**
* Prints a {@link TargetMethod} to this stream. If the target method has a non-null
* {@linkplain TargetMethod#classMethodActor}, then the output is of the form:
*
* <pre>
* <holder>.<name><descriptor> '{'<TargetMethod class>'@'<code start address>'}'
* </pre>
*
* Otherwise, it is of the form:
*
* <pre>
* <description> '{'<TargetMethod class>'@'<code start address>'}'
* </pre>
*
* @param tm the target method to print
* @param withNewline specifies if a newline should be appended to the stream after the target method
*/
public void printMethod(TargetMethod tm, boolean withNewline) {
if (tm.classMethodActor != null) {
printMethod(tm.classMethodActor, false);
} else {
print(tm.regionName(), false);
}
print(" {");
printSimpleName(ObjectAccess.readClassActor(tm).name.string);
print('@');
print(tm.codeStart());
print('}');
if (withNewline) {
println();
}
}
public void printSimpleName(String className) {
// cannot use substring as it allocates
int index = className.lastIndexOf('.');
for (int i = index + 1; i < className.length(); i++) {
print(className.charAt(i));
}
}
/**
* Prints a code location to this stream. If {@code tm} has a non-null
* {@linkplain TargetMethod#classMethodActor}, then the output is of the form:
*
* <pre>
* <ip> ' {' <TargetMethod class> ': ' <holder>.<name><descriptor> ' [' <code start> '+' <pos> ']}'
* </pre>
*
* Otherwise, it is of the form:
*
* <pre>
* <ip> ' {' <TargetMethod class> ': ' <description> ' [' <code start> '+' <pos> ']}'
* </pre>
*
* For example:
*
* <pre>
* 0x5c512856 {MaxTargetMethod: java.lang.String.toString()Ljava/lang/String; [0x5c512800+86]}
* 0x5c511946 {MaxTargetMethod: strampoline [0x5c511940+6]}
* </pre>
*
* @param tm the target method to print
* @param pos a position within {@code tm}
* @param withNewline specifies if a newline should be appended to the stream after the target method
*/
public void printLocation(TargetMethod tm, int pos, boolean withNewline) {
boolean lockDisabledSafepoints = lock();
CodePointer ip = tm.codeAt(pos);
print(ip);
print(" {");
printSimpleName(ObjectAccess.readClassActor(tm).name.string);
print(": ");
if (tm.classMethodActor != null) {
printMethod(tm.classMethodActor, false);
} else {
print(tm.regionName(), false);
}
print(" [");
print(tm.codeStart());
if (pos >= 0) {
print('+');
}
print(pos);
print("]}", withNewline);
unlock(lockDisabledSafepoints);
}
/**
* Convenience routine for printing a {@link Hub} to this stream. The output is of the form:
*
* <pre>
* {Static|Dynamic}Hub[<class name>]
* </pre>
*
* @param hub the hub to print
* @param withNewLine specifies if a newline should be appended to the stream after the hub
*/
public void printHub(Hub hub, boolean withNewLine) {
if (hub instanceof StaticHub) {
Log.print("Static");
} else {
Log.print("Dynamic");
}
Log.print("Hub[");
Log.print(hub.classActor.name());
Log.print(']');
if (withNewLine) {
Log.println();
}
}
/**
* Convenience routine for printing a {@link VmThread} to this stream. The output is of the form:
*
* <pre>
* <name>[<id>]
* </pre>
*
* For example, the output for the main thread of execution may be:
*
* <pre>
* "main[id=1]"
* </pre>
* @param vmThread the thread to print
* @param withNewline specifies if a newline should be appended to the stream after the thread
*/
public void printThread(VmThread vmThread, boolean withNewline) {
boolean lockDisabledSafepoints = false;
if (!MaxineVM.isHosted()) {
lockDisabledSafepoints = lock();
}
print(vmThread == null ? "<null thread>" : vmThread.getName());
print("[id=");
if (vmThread == null) {
print("?");
} else {
print(vmThread.id());
}
print("]", withNewline);
if (!MaxineVM.isHosted()) {
unlock(lockDisabledSafepoints);
}
}
/**
* Prints {@linkplain VmThreadLocal VM thread locals} to this stream.
*
* @param tla a pointer to VM thread locals
* @param all specifies if all 3 {@linkplain VmThreadLocal TLS} areas are to be printed
*/
public void printThreadLocals(Pointer tla, boolean all) {
boolean lockDisabledSafepoints = false;
if (!MaxineVM.isHosted()) {
lockDisabledSafepoints = lock();
}
if (!all) {
final List<VmThreadLocal> values = VmThreadLocal.values();
for (int i = 0; i != values.size(); i++) {
final VmThreadLocal vmThreadLocal = values.get(i);
for (int j = 0; j < 45 - vmThreadLocal.name.length(); j++) {
print(' ');
}
print(vmThreadLocal.name);
print(": ");
vmThreadLocal.log(this, tla, false);
println();
}
} else {
final List<VmThreadLocal> values = VmThreadLocal.values();
final Pointer enabled = ETLA.load(tla);
final Pointer disabled = DTLA.load(tla);
final Pointer triggered = TTLA.load(tla);
for (int i = 0; i != values.size(); i++) {
final VmThreadLocal vmThreadLocal = values.get(i);
for (int j = 0; j < 45 - vmThreadLocal.name.length(); j++) {
print(' ');
}
print(vmThreadLocal.name);
print(": {E} ");
vmThreadLocal.log(this, enabled, false);
print(" {D} ");
vmThreadLocal.log(this, disabled, false);
print(" {T} ");
vmThreadLocal.log(this, triggered, false);
println();
}
}
if (!MaxineVM.isHosted()) {
unlock(lockDisabledSafepoints);
}
}
/**
* Print a size with using unit suffixes to reduce the
* number of digits to three or less using base 2 for sizes.
* The conversion is allocation free.
*
* @param size the size to print
*/
public void printToPowerOfTwoUnits(Size size) {
long number = size.toLong();
if (number >= Longs.P) {
print(number / Longs.P); print('P');
} else if (number >= Longs.T) {
print(number / Longs.T); print('T');
} else if (number >= Longs.G) {
print(number / Longs.G); print('G');
} else if (number >= Longs.M) {
print(number / Longs.M); print('M');
} else if (number >= Longs.K) {
print(number / Longs.K); print('K');
} else {
print(number);
}
}
/**
* Print a size with using unit suffixes to reduce the
* number of digits to three or less using base 2 for sizes.
* The conversion is allocation free.
*
* @param size the size to print
*/
public void printlnToPowerOfTwoUnits(Size size) {
long number = size.toLong();
if (number >= Longs.P) {
print(number / Longs.P); println('P');
} else if (number >= Longs.T) {
print(number / Longs.T); println('T');
} else if (number >= Longs.G) {
print(number / Longs.G); println('G');
} else if (number >= Longs.M) {
print(number / Longs.M); println('M');
} else if (number >= Longs.K) {
print(number / Longs.K); println('K');
} else {
println(number);
}
}
}
private static VmThread lockOwner;
private static int lockDepth;
/**
* Gets the thread that current holds the log lock.
*/
public static VmThread lockOwner() {
return lockOwner;
}
/**
* Attempts to acquire the global lock on all debug output, blocking until the lock is successfully acquired. This
* lock can be acquired recursively by a thread. The lock is not released for other threads until the thread that
* owns the lock calls {@link #unlock} the same number of times it called this method.
*
* This method ensures that safepoints are disabled before it returns.
*
* @return true if this call caused safepoints to be disabled (i.e. they were enabled upon entry to this method).
* This value must be passed to the paired call to {@link #unlock} so that safepoints are restored to the
* appropriate state (i.e. the state they had before the sequence of code protected by this lock was
* entered).
*/
public static boolean lock() {
if (isHosted()) {
return true;
}
boolean wasDisabled = SafepointPoll.disable();
Log.log_lock();
if (lockDepth == 0) {
FatalError.check(lockOwner == null, "log lock should have no owner with depth 0");
lockOwner = VmThread.current();
}
lockDepth++;
return !wasDisabled;
}
/**
* Attempts to releases the global lock on all debug output. This must only be called by a thread that currently
* owns the lock - failure to do so causes the VM to exit. The lock is not released for other threads until this
* method is called the same number of times as {@link #lock()} was called when acquiring the lock.
*
* @param lockDisabledSafepoints specifies if the adjoining call to {@link #lock()} disabled safepoints. If so, then
* this call will re-enable them.
*/
public static void unlock(boolean lockDisabledSafepoints) {
if (isHosted()) {
return;
}
--lockDepth;
FatalError.check(lockDepth >= 0, "mismatched lock/unlock");
FatalError.check(lockOwner == VmThread.current(), "log lock should be owned by current thread");
if (lockDepth == 0) {
lockOwner = null;
}
Log.log_unlock();
ProgramError.check(SafepointPoll.isDisabled(), "Safepoints must not be re-enabled in code surrounded by Log.lock() and Log.unlock()");
if (lockDisabledSafepoints) {
SafepointPoll.enable();
}
}
static {
ProgramWarning.setHandler(new Handler() {
public void handle(String message) {
if (MaxineVM.isHosted()) {
System.err.println(message);
} else {
Log.println(message);
}
}
});
}
}