/* * Copyright (c) 1997, 2022, 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. *
*/
// Prints the current bytecode and its attributes using bytecode-specific information.
class BytecodePrinter { private: // %%% This field is not GC-ed, and so can contain garbage // between critical sections. Use only pointer-comparison // operations on the pointer, except within a critical section. // (Also, ensure that occasional false positives are benign.)
Method* _current_method; bool _is_wide;
Bytecodes::Code _code;
address _next_pc; // current decoding position int _flags;
void align() { _next_pc = align_up(_next_pc, sizeof(jint)); } int get_byte() { return *(jbyte*) _next_pc++; } // signed short get_short() { short i=Bytes::get_Java_u2(_next_pc); _next_pc+=2; return i; } int get_int() { int i=Bytes::get_Java_u4(_next_pc); _next_pc+=4; return i; }
int get_index_u1() { return *(address)_next_pc++; } int get_index_u2() { int i=Bytes::get_Java_u2(_next_pc); _next_pc+=2; return i; } int get_index_u1_cpcache() { return get_index_u1() + ConstantPool::CPCACHE_INDEX_TAG; } int get_index_u2_cpcache() { int i=Bytes::get_native_u2(_next_pc); _next_pc+=2; returni + ConstantPool::CPCACHE_INDEX_TAG; } int get_index_u4() { int i=Bytes::get_native_u4(_next_pc); _next_pc+=4; return i; } int get_index_special() { return (is_wide()) ? get_index_u2() : get_index_u1(); }
Method* method() { return _current_method; } bool is_wide() { return _is_wide; }
Bytecodes::Code raw_code() { return Bytecodes::Code(_code); }
bool check_index(int i, int& cp_index, outputStream* st); bool check_cp_cache_index(int i, int& cp_index, outputStream* st); bool check_obj_index(int i, int& cp_index, outputStream* st); bool check_invokedynamic_index(int i, int& cp_index, outputStream* st); void print_constant(int i, outputStream* st); void print_constant_nocheck(int i, outputStream* st); void print_cpcache_entry(int cpc_index, outputStream* st); void print_dynamic(int orig_i, int i, constantTag tag, outputStream* st); void print_field_or_method(int i, outputStream* st); void print_field_or_method(int orig_i, int i, outputStream* st); void print_invoke_handle(int i, outputStream* st); void print_attributes(int bci, outputStream* st); void bytecode_epilog(int bci, outputStream* st);
// This method is called while executing the raw bytecodes, so none of // the adjustments that BytecodeStream performs applies. void trace(const methodHandle& method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st) {
ResourceMark rm; if (_current_method != method()) { // Note 1: This code will not work as expected with true MT/MP. // Need an explicit lock or a different solution. // It is possible for this block to be skipped, if a garbage // _current_method pointer happens to have the same bits as // the incoming method. We could lose a line of trace output. // This is acceptable in a debug-only feature.
st->cr();
st->print("[%ld] ", (long) Thread::current()->osthread()->thread_id());
method->print_name(st);
st->cr();
_current_method = method();
}
Bytecodes::Code code; if (is_wide()) { // bcp wasn't advanced if previous bytecode was _wide.
code = Bytecodes::code_at(method(), bcp+1);
} else {
code = Bytecodes::code_at(method(), bcp);
}
_code = code; int bci = bcp - method->code_base();
st->print("[%ld] ", (long) Thread::current()->osthread()->thread_id()); if (Verbose) {
st->print("%8d %4d " INTPTR_FORMAT " " INTPTR_FORMAT " %s",
BytecodeCounter::counter_value(), bci, tos, tos2, Bytecodes::name(code));
} else {
st->print("%8d %4d %s",
BytecodeCounter::counter_value(), bci, Bytecodes::name(code));
}
_next_pc = is_wide() ? bcp+2 : bcp+1;
print_attributes(bci, st); // Set is_wide for the next one, since the caller of this doesn't skip // the next bytecode.
_is_wide = (code == Bytecodes::_wide);
_code = Bytecodes::_illegal;
}
// Used for Method*::print_codes(). The input bcp comes from // BytecodeStream, which will skip wide bytecodes. void trace(const methodHandle& method, address bcp, outputStream* st) {
_current_method = method();
ResourceMark rm;
Bytecodes::Code code = Bytecodes::code_at(method(), bcp); // Set is_wide
_is_wide = (code == Bytecodes::_wide); if (is_wide()) {
code = Bytecodes::code_at(method(), bcp+1);
}
_code = code; int bci = bcp - method->code_base(); // Print bytecode index and name if (ClassPrinter::has_mode(_flags, ClassPrinter::PRINT_BYTECODE_ADDR)) {
st->print(INTPTR_FORMAT " ", p2i(bcp));
} if (is_wide()) {
st->print("%4d %s_w", bci, Bytecodes::name(code));
} else {
st->print("%4d %s", bci, Bytecodes::name(code));
}
_next_pc = is_wide() ? bcp+2 : bcp+1;
print_attributes(bci, st);
bytecode_epilog(bci, st);
}
};
// We need a global instance to keep track of the states when the bytecodes // are executed. Access by multiple threads are controlled by ttyLocker. static BytecodePrinter _interpreter_printer;
void BytecodeTracer::trace_interpreter(const methodHandle& method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st) { if (TraceBytecodes && BytecodeCounter::counter_value() >= TraceBytecodesAt) {
ttyLocker ttyl; // 5065316: keep the following output coherent // The ttyLocker also prevents races between two threads // trying to use the single instance of BytecodePrinter. // // There used to be a leaf mutex here, but the ttyLocker will // work just as well, as long as the printing operations never block.
_interpreter_printer.trace(method, bcp, tos, tos2, st);
}
}
void BytecodeTracer::print_method_codes(const methodHandle& method, int from, int to, outputStream* st, int flags) {
BytecodePrinter method_printer(flags);
BytecodeStream s(method);
s.set_interval(from, to);
ttyLocker ttyl; // keep the following output coherent while (s.next() >= 0) {
method_printer.trace(method, s.bcp(), st);
}
}
bool BytecodePrinter::check_index(int i, int& cp_index, outputStream* st) {
ConstantPool* constants = method()->constants(); int ilimit = constants->length();
Bytecodes::Code code = raw_code();
if (Bytecodes::uses_cp_cache(code)) { bool okay = true; switch (code) { case Bytecodes::_fast_aldc: case Bytecodes::_fast_aldc_w:
okay = check_obj_index(i, cp_index, st); break; case Bytecodes::_invokedynamic:
okay = check_invokedynamic_index(i, cp_index, st); break; default:
okay = check_cp_cache_index(i, cp_index, st); break;
} if (!okay) returnfalse;
}
// check cp index if (cp_index >= 0 && cp_index < ilimit) { if (WizardMode) st->print(" cp[%d]", cp_index); returntrue;
}
st->print_cr(" CP[%d] not in CP", cp_index); returnfalse;
}
bool BytecodePrinter::check_cp_cache_index(int i, int& cp_index, outputStream* st) {
ConstantPool* constants = method()->constants(); int climit = 0;
ConstantPoolCache* cache = constants->cache(); // If rewriter hasn't run, the index is the cp_index if (cache == NULL) {
cp_index = i; returntrue;
} //climit = cache->length(); // %%% private!
size_t size = cache->size() * wordSize;
size -= sizeof(ConstantPoolCache);
size /= sizeof(ConstantPoolCacheEntry);
climit = (int) size;
#ifdef ASSERT
{ constint CPCACHE_INDEX_TAG = ConstantPool::CPCACHE_INDEX_TAG; if (i >= CPCACHE_INDEX_TAG && i < climit + CPCACHE_INDEX_TAG) {
i -= CPCACHE_INDEX_TAG;
} else {
st->print_cr(" CP[%d] missing bias?", i); returnfalse;
}
} #endif//ASSERT if (i >= 0 && i < climit) {
cp_index = cache->entry_at(i)->constant_pool_index();
} else {
st->print_cr("%d not in CP[*]?", i); returnfalse;
} returntrue;
}
bool BytecodePrinter::check_obj_index(int i, int& cp_index, outputStream* st) {
ConstantPool* constants = method()->constants();
i -= ConstantPool::CPCACHE_INDEX_TAG;
if (i >= 0 && i < constants->resolved_references()->length()) {
cp_index = constants->object_to_cp_index(i); returntrue;
} else {
st->print_cr("%d not in OBJ[*]?", i); returnfalse;
}
}
bool BytecodePrinter::check_invokedynamic_index(int i, int& cp_index, outputStream* st) {
assert(ConstantPool::is_invokedynamic_index(i), "not secondary index?");
i = ConstantPool::decode_invokedynamic_index(i) + ConstantPool::CPCACHE_INDEX_TAG;
return check_cp_cache_index(i, cp_index, st);
}
void BytecodePrinter::print_constant(int i, outputStream* st) { int orig_i = i; if (!check_index(orig_i, i, st)) return;
print_constant_nocheck(i, st);
}
void BytecodePrinter::print_constant_nocheck(int i, outputStream* st) {
ConstantPool* constants = method()->constants();
constantTag tag = constants->tag_at(i);
void BytecodePrinter::print_field_or_method(int i, outputStream* st) { int orig_i = i; if (!check_index(orig_i, i, st)) return;
print_field_or_method(orig_i, i, st);
}
void BytecodePrinter::print_field_or_method(int orig_i, int i, outputStream* st) {
ConstantPool* constants = method()->constants();
constantTag tag = constants->tag_at(i);
bool has_klass = true;
switch (tag.value()) { case JVM_CONSTANT_InterfaceMethodref: case JVM_CONSTANT_Methodref: case JVM_CONSTANT_Fieldref: break; case JVM_CONSTANT_NameAndType: case JVM_CONSTANT_Dynamic: case JVM_CONSTANT_InvokeDynamic:
has_klass = false; break; default:
st->print_cr(" bad tag=%d at %d", tag.value(), i); return;
}
Symbol* name = constants->uncached_name_ref_at(i);
Symbol* signature = constants->uncached_signature_ref_at(i); constchar* sep = (tag.is_field() ? "/" : ""); if (has_klass) {
Symbol* klass = constants->klass_name_at(constants->uncached_klass_ref_index_at(i));
st->print_cr(" %d <%s.%s%s%s> ", i, klass->as_C_string(), name->as_C_string(), sep, signature->as_C_string());
} else { if (tag.is_dynamic_constant() || tag.is_invoke_dynamic()) { int bsm = constants->bootstrap_method_ref_index_at(i);
st->print(" bsm=%d", bsm);
}
st->print_cr(" %d <%s%s%s>", i, name->as_C_string(), sep, signature->as_C_string());
}
if (ClassPrinter::has_mode(_flags, ClassPrinter::PRINT_DYNAMIC) &&
(tag.is_dynamic_constant() || tag.is_invoke_dynamic())) {
print_dynamic(orig_i, i, tag, st);
}
}
void BytecodePrinter::print_dynamic(int orig_i, int bsm_cpindex, constantTag tag, outputStream* st) {
ConstantPool* constants = method()->constants(); int bsm = constants->bootstrap_method_ref_index_at(bsm_cpindex); constchar* ref_kind = ""; switch (constants->method_handle_ref_kind_at(bsm)) { case JVM_REF_getField : ref_kind = "REF_getField"; break; case JVM_REF_getStatic : ref_kind = "REF_getStatic"; break; case JVM_REF_putField : ref_kind = "REF_putField"; break; case JVM_REF_putStatic : ref_kind = "REF_putStatic"; break; case JVM_REF_invokeVirtual : ref_kind = "REF_invokeVirtual"; break; case JVM_REF_invokeStatic : ref_kind = "REF_invokeStatic"; break; case JVM_REF_invokeSpecial : ref_kind = "REF_invokeSpecial"; break; case JVM_REF_newInvokeSpecial : ref_kind = "REF_newInvokeSpecial"; break; case JVM_REF_invokeInterface : ref_kind = "REF_invokeInterface"; break; default : ShouldNotReachHere();
}
st->print(" BSM: %s", ref_kind);
print_field_or_method(-1, constants->method_handle_index_at(bsm), st); int argc = constants->bootstrap_argument_count_at(bsm_cpindex);
st->print(" arguments[%d] = {", argc); if (argc > 0) {
st->cr(); for (int arg_i = 0; arg_i < argc; arg_i++) { int arg = constants->bootstrap_argument_index_at(bsm_cpindex, arg_i);
st->print(" ");
print_constant_nocheck(arg, st);
}
}
st->print_cr(" }"); if (tag.is_invoke_dynamic()) { int indy_index = orig_i; int cpc_index = constants->invokedynamic_cp_cache_index(indy_index);
print_cpcache_entry(cpc_index, st);
} else { // TODO: print info for tag.is_dynamic_constant()
}
}
void BytecodePrinter::print_invoke_handle(int i, outputStream* st) {
print_cpcache_entry(ConstantPool::decode_cpcache_index(i), st);
}
void BytecodePrinter::print_attributes(int bci, outputStream* st) { // Show attributes of pre-rewritten codes
Bytecodes::Code code = Bytecodes::java_code(raw_code()); // If the code doesn't have any fields there's nothing to print. // note this is ==1 because the tableswitch and lookupswitch are // zero size (for some reason) and we want to print stuff out for them. if (Bytecodes::length_for(code) == 1) {
st->cr(); return;
}
switch(code) { // Java specific bytecodes only matter. case Bytecodes::_bipush:
st->print_cr(" " INT32_FORMAT, get_byte()); break; case Bytecodes::_sipush:
st->print_cr(" " INT32_FORMAT, get_short()); break; case Bytecodes::_ldc: if (Bytecodes::uses_cp_cache(raw_code())) {
print_constant(get_index_u1_cpcache(), st);
} else {
print_constant(get_index_u1(), st);
} break;
case Bytecodes::_ldc_w: case Bytecodes::_ldc2_w: if (Bytecodes::uses_cp_cache(raw_code())) {
print_constant(get_index_u2_cpcache(), st);
} else {
print_constant(get_index_u2(), st);
} break;
case Bytecodes::_iload: case Bytecodes::_lload: case Bytecodes::_fload: case Bytecodes::_dload: case Bytecodes::_aload: case Bytecodes::_istore: case Bytecodes::_lstore: case Bytecodes::_fstore: case Bytecodes::_dstore: case Bytecodes::_astore:
st->print_cr(" #%d", get_index_special()); break;
case Bytecodes::_iinc:
{ int index = get_index_special();
jint offset = is_wide() ? get_short(): get_byte();
st->print_cr(" #%d " INT32_FORMAT, index, offset);
} break;
case Bytecodes::_newarray: {
BasicType atype = (BasicType)get_index_u1(); constchar* str = type2name(atype); if (str == NULL || is_reference_type(atype)) {
assert(false, "Unidentified basic type");
}
st->print_cr(" %s", str);
} break; case Bytecodes::_anewarray: { int klass_index = get_index_u2();
ConstantPool* constants = method()->constants();
Symbol* name = constants->klass_name_at(klass_index);
st->print_cr(" %s ", name->as_C_string());
} break; case Bytecodes::_multianewarray: { int klass_index = get_index_u2(); int nof_dims = get_index_u1();
ConstantPool* constants = method()->constants();
Symbol* name = constants->klass_name_at(klass_index);
st->print_cr(" %s %d", name->as_C_string(), nof_dims);
} break;
case Bytecodes::_ifeq: case Bytecodes::_ifnull: case Bytecodes::_iflt: case Bytecodes::_ifle: case Bytecodes::_ifne: case Bytecodes::_ifnonnull: case Bytecodes::_ifgt: case Bytecodes::_ifge: case Bytecodes::_if_icmpeq: case Bytecodes::_if_icmpne: case Bytecodes::_if_icmplt: case Bytecodes::_if_icmpgt: case Bytecodes::_if_icmple: case Bytecodes::_if_icmpge: case Bytecodes::_if_acmpeq: case Bytecodes::_if_acmpne: case Bytecodes::_goto: case Bytecodes::_jsr:
st->print_cr(" %d", bci + get_short()); break;
case Bytecodes::_goto_w: case Bytecodes::_jsr_w:
st->print_cr(" %d", bci + get_int()); break;
case Bytecodes::_ret: st->print_cr(" %d", get_index_special()); break;
case Bytecodes::_tableswitch:
{ align(); int default_dest = bci + get_int(); int lo = get_int(); int hi = get_int(); int len = hi - lo + 1;
jint* dest = NEW_RESOURCE_ARRAY(jint, len); for (int i = 0; i < len; i++) {
dest[i] = bci + get_int();
}
st->print(" %d " INT32_FORMAT " " INT32_FORMAT " ",
default_dest, lo, hi); constchar *comma = ""; for (int ll = lo; ll <= hi; ll++) { int idx = ll - lo;
st->print("%s %d:" INT32_FORMAT " (delta: %d)", comma, ll, dest[idx], dest[idx]-bci);
comma = ",";
}
st->cr();
} break; case Bytecodes::_lookupswitch:
{ align(); int default_dest = bci + get_int(); int len = get_int();
jint* key = NEW_RESOURCE_ARRAY(jint, len);
jint* dest = NEW_RESOURCE_ARRAY(jint, len); for (int i = 0; i < len; i++) {
key [i] = get_int();
dest[i] = bci + get_int();
};
st->print(" %d %d ", default_dest, len); constchar *comma = ""; for (int ll = 0; ll < len; ll++) {
st->print("%s " INT32_FORMAT ":" INT32_FORMAT, comma, key[ll], dest[ll]);
comma = ",";
}
st->cr();
} break;
case Bytecodes::_putstatic: case Bytecodes::_getstatic: case Bytecodes::_putfield: case Bytecodes::_getfield:
print_field_or_method(get_index_u2_cpcache(), st); break;
case Bytecodes::_invokevirtual: case Bytecodes::_invokespecial: case Bytecodes::_invokestatic:
{ int i = get_index_u2_cpcache();
print_field_or_method(i, st); if (raw_code() == Bytecodes::_invokehandle &&
ClassPrinter::has_mode(_flags, ClassPrinter::PRINT_METHOD_HANDLE)) {
print_invoke_handle(i, st);
}
} break;
case Bytecodes::_invokeinterface:
{ int i = get_index_u2_cpcache(); int n = get_index_u1();
get_byte(); // ignore zero byte
print_field_or_method(i, st);
} break;
case Bytecodes::_invokedynamic:
print_field_or_method(get_index_u4(), st); break;
case Bytecodes::_new: case Bytecodes::_checkcast: case Bytecodes::_instanceof:
{ int i = get_index_u2();
ConstantPool* constants = method()->constants();
Symbol* name = constants->klass_name_at(i);
st->print_cr(" %d <%s>", i, name->as_C_string());
} break;
case Bytecodes::_wide: // length is zero not one, but printed with no more info. break;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.