8292699: Improve printing of classes in native debugger
Reviewed-by: coleenp
This commit is contained in:
parent
7520d0a725
commit
89dafc002f
178
src/hotspot/share/classfile/classPrinter.cpp
Normal file
178
src/hotspot/share/classfile/classPrinter.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/classLoaderData.hpp"
|
||||
#include "classfile/classLoaderDataGraph.hpp"
|
||||
#include "classfile/classPrinter.hpp"
|
||||
#include "memory/iterator.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/instanceKlass.hpp"
|
||||
#include "oops/klass.hpp"
|
||||
#include "oops/method.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
|
||||
class ClassPrinter::KlassPrintClosure : public LockedClassesDo {
|
||||
const char* _class_name_pattern;
|
||||
const char* _method_name_pattern;
|
||||
const char* _method_signature_pattern;
|
||||
bool _always_print_class_name;
|
||||
int _flags;
|
||||
outputStream* _st;
|
||||
int _num;
|
||||
bool _has_printed_methods;
|
||||
public:
|
||||
KlassPrintClosure(const char* class_name_pattern,
|
||||
const char* method_name_pattern,
|
||||
const char* method_signature_pattern,
|
||||
bool always_print_class_name,
|
||||
int flags, outputStream* st)
|
||||
: _class_name_pattern(class_name_pattern),
|
||||
_method_name_pattern(method_name_pattern),
|
||||
_method_signature_pattern(method_signature_pattern),
|
||||
_always_print_class_name(always_print_class_name),
|
||||
_flags(flags), _st(st), _num(0), _has_printed_methods(false)
|
||||
{
|
||||
if (has_mode(_flags, PRINT_METHOD_HANDLE)) {
|
||||
_flags |= (PRINT_METHOD_NAME | PRINT_BYTECODE);
|
||||
}
|
||||
if (has_mode(_flags, PRINT_DYNAMIC)) {
|
||||
_flags |= (PRINT_METHOD_NAME | PRINT_BYTECODE);
|
||||
}
|
||||
if (has_mode(_flags, PRINT_BYTECODE_ADDR)) {
|
||||
_flags |= (PRINT_METHOD_NAME | PRINT_BYTECODE);
|
||||
}
|
||||
if (has_mode(_flags, PRINT_BYTECODE)) {
|
||||
_flags |= (PRINT_METHOD_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void do_klass(Klass* k) {
|
||||
if (!k->is_instance_klass()) {
|
||||
return;
|
||||
}
|
||||
print_instance_klass(InstanceKlass::cast(k));
|
||||
}
|
||||
|
||||
static bool match(const char* pattern, Symbol* sym) {
|
||||
return (pattern == NULL || sym->is_star_match(pattern));
|
||||
}
|
||||
|
||||
void print_klass_name(InstanceKlass* ik) {
|
||||
_st->print("[%3d] " INTPTR_FORMAT " class %s ", _num++, p2i(ik), ik->name()->as_C_string());
|
||||
ik->class_loader_data()->print_value_on(_st);
|
||||
_st->cr();
|
||||
}
|
||||
|
||||
void print_instance_klass(InstanceKlass* ik) {
|
||||
if (ik->is_loaded() && ik->name()->is_star_match(_class_name_pattern)) {
|
||||
ResourceMark rm;
|
||||
if (_has_printed_methods) {
|
||||
// We have printed some methods in the previous class.
|
||||
// Print a new line to separate the two classes
|
||||
_st->cr();
|
||||
}
|
||||
_has_printed_methods = false;
|
||||
if (_always_print_class_name) {
|
||||
print_klass_name(ik);
|
||||
}
|
||||
|
||||
if (has_mode(_flags, ClassPrinter::PRINT_METHOD_NAME)) {
|
||||
bool print_codes = has_mode(_flags, ClassPrinter::PRINT_BYTECODE);
|
||||
int len = ik->methods()->length();
|
||||
int num_methods_printed = 0;
|
||||
|
||||
for (int index = 0; index < len; index++) {
|
||||
Method* m = ik->methods()->at(index);
|
||||
if (match(_method_name_pattern, m->name()) &&
|
||||
match(_method_signature_pattern, m->signature())) {
|
||||
if (print_codes && num_methods_printed++ > 0) {
|
||||
_st->cr();
|
||||
}
|
||||
|
||||
if (_has_printed_methods == false) {
|
||||
if (!_always_print_class_name) {
|
||||
print_klass_name(ik);
|
||||
}
|
||||
_has_printed_methods = true;
|
||||
}
|
||||
print_method(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void print_method(Method* m) {
|
||||
bool print_codes = has_mode(_flags, ClassPrinter::PRINT_BYTECODE);
|
||||
_st->print_cr(INTPTR_FORMAT " %smethod %s : %s", p2i(m),
|
||||
m->is_static() ? "static " : "",
|
||||
m->name()->as_C_string(), m->signature()->as_C_string());
|
||||
if (print_codes) {
|
||||
m->print_codes_on(_st, _flags);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void ClassPrinter::print_flags_help(outputStream* os) {
|
||||
os->print_cr("flags (bitmask):");
|
||||
os->print_cr(" 0x%02x - print names of methods", PRINT_METHOD_NAME);
|
||||
os->print_cr(" 0x%02x - print bytecodes", PRINT_BYTECODE);
|
||||
os->print_cr(" 0x%02x - print the address of bytecodes", PRINT_BYTECODE_ADDR);
|
||||
os->print_cr(" 0x%02x - print info for invokedynamic", PRINT_DYNAMIC);
|
||||
os->print_cr(" 0x%02x - print info for invokehandle", PRINT_METHOD_HANDLE);
|
||||
os->cr();
|
||||
}
|
||||
|
||||
void ClassPrinter::print_classes(const char* class_name_pattern, int flags, outputStream* os) {
|
||||
KlassPrintClosure closure(class_name_pattern, NULL, NULL, true, flags, os);
|
||||
ClassLoaderDataGraph::classes_do(&closure);
|
||||
}
|
||||
|
||||
void ClassPrinter::print_methods(const char* class_name_pattern,
|
||||
const char* method_pattern, int flags, outputStream* os) {
|
||||
ResourceMark rm;
|
||||
const char* method_name_pattern;
|
||||
const char* method_signature_pattern;
|
||||
|
||||
const char* colon = strchr(method_pattern, ':');
|
||||
if (colon == NULL) {
|
||||
method_name_pattern = method_pattern;
|
||||
method_signature_pattern = NULL;
|
||||
} else {
|
||||
ptrdiff_t name_pat_len = colon - method_pattern;
|
||||
assert(name_pat_len >= 0, "sanity");
|
||||
char* buf = NEW_RESOURCE_ARRAY(char, name_pat_len + 1);
|
||||
strncpy(buf, method_pattern, name_pat_len);
|
||||
buf[name_pat_len] = 0;
|
||||
|
||||
method_name_pattern = buf;
|
||||
method_signature_pattern = colon + 1;
|
||||
}
|
||||
|
||||
KlassPrintClosure closure(class_name_pattern, method_name_pattern, method_signature_pattern,
|
||||
false, flags | PRINT_METHOD_NAME, os);
|
||||
ClassLoaderDataGraph::classes_do(&closure);
|
||||
}
|
68
src/hotspot/share/classfile/classPrinter.hpp
Normal file
68
src/hotspot/share/classfile/classPrinter.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_CLASSFILE_CLASSPRINTER_HPP
|
||||
#define SHARE_CLASSFILE_CLASSPRINTER_HPP
|
||||
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
class InstanceKlass;
|
||||
class Method;
|
||||
class outputStream;
|
||||
class Symbol;
|
||||
|
||||
// ClassPrinter is intended to be called from findclass() and findmethod()
|
||||
// in debug.cpp (inside a debugger, such as gdb).
|
||||
//
|
||||
// The ClassPrinter::print_xxx() functions hold the ClassLoaderDataGraph_lock
|
||||
// (and the ttyLocker if ClassPrinter::PRINT_BYTECODE is selected). A deadlock
|
||||
// may happen if these functions are called in a context where these locks
|
||||
// are already held. Use with caution.
|
||||
|
||||
class ClassPrinter : public AllStatic {
|
||||
class KlassPrintClosure;
|
||||
|
||||
public:
|
||||
|
||||
enum Mode : int {
|
||||
PRINT_METHOD_NAME = 1 << 0,
|
||||
PRINT_BYTECODE = 1 << 1,
|
||||
PRINT_BYTECODE_ADDR = 1 << 2,
|
||||
PRINT_DYNAMIC = 1 << 3, // extra information for invokedynamic (and dynamic constant ...)
|
||||
PRINT_METHOD_HANDLE = 1 << 4, // extra information for invokehandle
|
||||
};
|
||||
static bool has_mode(int flags, Mode mode) {
|
||||
return (flags & static_cast<int>(mode)) != 0;
|
||||
}
|
||||
|
||||
static void print_flags_help(outputStream* os);
|
||||
|
||||
// flags must be OR'ed from ClassPrinter::Mode for the these two functions
|
||||
static void print_classes(const char* class_name_pattern, int flags, outputStream* os);
|
||||
static void print_methods(const char* class_name_pattern,
|
||||
const char* method_name_pattern, int flags, outputStream* os);
|
||||
};
|
||||
|
||||
#endif // SHARE_CLASSFILE_CLASSPRINTER_HPP
|
@ -23,8 +23,10 @@
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/classPrinter.hpp"
|
||||
#include "classfile/javaClasses.inline.hpp"
|
||||
#include "interpreter/bytecodeHistogram.hpp"
|
||||
#include "interpreter/bytecodeStream.hpp"
|
||||
#include "interpreter/bytecodeTracer.hpp"
|
||||
#include "interpreter/bytecodes.hpp"
|
||||
#include "interpreter/interpreter.hpp"
|
||||
@ -33,16 +35,15 @@
|
||||
#include "oops/constantPool.inline.hpp"
|
||||
#include "oops/methodData.hpp"
|
||||
#include "oops/method.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/osThread.hpp"
|
||||
#include "runtime/timer.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
|
||||
// Prints the current bytecode and its attributes using bytecode-specific information.
|
||||
|
||||
// Standard closure for BytecodeTracer: prints the current bytecode
|
||||
// and its attributes using bytecode-specific information.
|
||||
|
||||
class BytecodePrinter: public BytecodeClosure {
|
||||
class BytecodePrinter {
|
||||
private:
|
||||
// %%% This field is not GC-ed, and so can contain garbage
|
||||
// between critical sections. Use only pointer-comparison
|
||||
@ -52,6 +53,7 @@ class BytecodePrinter: public BytecodeClosure {
|
||||
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
|
||||
@ -69,20 +71,25 @@ class BytecodePrinter: public BytecodeClosure {
|
||||
Bytecodes::Code raw_code() { return Bytecodes::Code(_code); }
|
||||
|
||||
|
||||
bool check_index(int i, int& cp_index, outputStream* st = tty);
|
||||
bool check_cp_cache_index(int i, int& cp_index, outputStream* st = tty);
|
||||
bool check_obj_index(int i, int& cp_index, outputStream* st = tty);
|
||||
bool check_invokedynamic_index(int i, int& cp_index, outputStream* st = tty);
|
||||
void print_constant(int i, outputStream* st = tty);
|
||||
void print_field_or_method(int i, outputStream* st = tty);
|
||||
void print_field_or_method(int orig_i, int i, outputStream* st = tty);
|
||||
void print_attributes(int bci, outputStream* st = tty);
|
||||
void bytecode_epilog(int bci, outputStream* st = tty);
|
||||
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);
|
||||
|
||||
public:
|
||||
BytecodePrinter() {
|
||||
BytecodePrinter(int flags = 0) {
|
||||
_is_wide = false;
|
||||
_code = Bytecodes::_illegal;
|
||||
_flags = flags;
|
||||
}
|
||||
|
||||
// This method is called while executing the raw bytecodes, so none of
|
||||
@ -120,7 +127,7 @@ class BytecodePrinter: public BytecodeClosure {
|
||||
BytecodeCounter::counter_value(), bci, Bytecodes::name(code));
|
||||
}
|
||||
_next_pc = is_wide() ? bcp+2 : bcp+1;
|
||||
print_attributes(bci);
|
||||
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);
|
||||
@ -141,10 +148,13 @@ class BytecodePrinter: public BytecodeClosure {
|
||||
_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("%d %s_w", bci, Bytecodes::name(code));
|
||||
st->print("%4d %s_w", bci, Bytecodes::name(code));
|
||||
} else {
|
||||
st->print("%d %s", bci, Bytecodes::name(code));
|
||||
st->print("%4d %s", bci, Bytecodes::name(code));
|
||||
}
|
||||
_next_pc = is_wide() ? bcp+2 : bcp+1;
|
||||
print_attributes(bci, st);
|
||||
@ -152,53 +162,31 @@ class BytecodePrinter: public BytecodeClosure {
|
||||
}
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
// Implementation of BytecodeTracer
|
||||
|
||||
// %%% This set_closure thing seems overly general, given that
|
||||
// nobody uses it. Also, if BytecodePrinter weren't hidden
|
||||
// then Method* could use instances of it directly and it
|
||||
// would be easier to remove races on _current_method and bcp.
|
||||
// Since this is not product functionality, we can defer cleanup.
|
||||
|
||||
BytecodeClosure* BytecodeTracer::_closure = NULL;
|
||||
|
||||
static BytecodePrinter std_closure;
|
||||
BytecodeClosure* BytecodeTracer::std_closure() {
|
||||
return &::std_closure;
|
||||
}
|
||||
|
||||
|
||||
void BytecodeTracer::trace(const methodHandle& method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st) {
|
||||
if (_closure == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
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.
|
||||
// Using the ttyLocker prevents the system from coming to
|
||||
// a safepoint within this code, which is sensitive to Method*
|
||||
// movement.
|
||||
//
|
||||
// There used to be a leaf mutex here, but the ttyLocker will
|
||||
// work just as well, as long as the printing operations never block.
|
||||
//
|
||||
// We put the locker on the static trace method, not the
|
||||
// virtual one, because the clients of this module go through
|
||||
// the static method.
|
||||
_closure->trace(method, bcp, tos, tos2, st);
|
||||
_interpreter_printer.trace(method, bcp, tos, tos2, st);
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeTracer::trace(const methodHandle& method, address bcp, outputStream* st) {
|
||||
if (_closure == NULL) {
|
||||
return;
|
||||
}
|
||||
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; // 5065316: keep the following output coherent
|
||||
_closure->trace(method, bcp, st);
|
||||
ttyLocker ttyl; // keep the following output coherent
|
||||
while (s.next() >= 0) {
|
||||
method_printer.trace(method, s.bcp(), st);
|
||||
}
|
||||
}
|
||||
|
||||
void print_symbol(Symbol* sym, outputStream* st) {
|
||||
@ -324,6 +312,10 @@ 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);
|
||||
|
||||
@ -396,8 +388,61 @@ void BytecodePrinter::print_field_or_method(int orig_i, int i, outputStream* st)
|
||||
}
|
||||
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);
|
||||
const char* 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_cpcache_entry(int cpc_index, outputStream* st) {
|
||||
ConstantPool* constants = method()->constants();
|
||||
ConstantPoolCacheEntry* cpce = constants->cache()->entry_at(cpc_index);
|
||||
st->print(" ConstantPoolCacheEntry: ");
|
||||
cpce->print(st, cpc_index, constants->cache());
|
||||
}
|
||||
|
||||
void BytecodePrinter::print_attributes(int bci, outputStream* st) {
|
||||
// Show attributes of pre-rewritten codes
|
||||
@ -559,7 +604,14 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) {
|
||||
case Bytecodes::_invokevirtual:
|
||||
case Bytecodes::_invokespecial:
|
||||
case Bytecodes::_invokestatic:
|
||||
print_field_or_method(get_index_u2_cpcache(), st);
|
||||
{
|
||||
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:
|
||||
|
@ -26,40 +26,20 @@
|
||||
#define SHARE_INTERPRETER_BYTECODETRACER_HPP
|
||||
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
// The BytecodeTracer is a helper class used by the interpreter for run-time
|
||||
// bytecode tracing. If bytecode tracing is turned on, trace() will be called
|
||||
// bytecode tracing. If TraceBytecodes turned on, trace_interpreter() will be called
|
||||
// for each bytecode.
|
||||
//
|
||||
// By specialising the BytecodeClosure, all kinds of bytecode traces can
|
||||
// be done.
|
||||
|
||||
// class BytecodeTracer is used by TraceBytecodes option and PrintMethodData
|
||||
|
||||
class methodHandle;
|
||||
class outputStream;
|
||||
|
||||
class BytecodeClosure;
|
||||
class BytecodeTracer: AllStatic {
|
||||
private:
|
||||
static BytecodeClosure* _closure;
|
||||
|
||||
public:
|
||||
static BytecodeClosure* std_closure(); // a printing closure
|
||||
static BytecodeClosure* closure() { return _closure; }
|
||||
static void set_closure(BytecodeClosure* closure) { _closure = closure; }
|
||||
|
||||
static void trace(const methodHandle& method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st = tty);
|
||||
static void trace(const methodHandle& method, address bcp, outputStream* st = tty);
|
||||
};
|
||||
|
||||
|
||||
// For each bytecode, a BytecodeClosure's trace() routine will be called.
|
||||
|
||||
class BytecodeClosure {
|
||||
public:
|
||||
virtual void trace(const methodHandle& method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st) = 0;
|
||||
virtual void trace(const methodHandle& method, address bcp, outputStream* st) = 0;
|
||||
static void trace_interpreter(const methodHandle& method, address bcp, uintptr_t tos, uintptr_t tos2, outputStream* st = tty);
|
||||
static void print_method_codes(const methodHandle& method, int from, int to, outputStream* st, int flags);
|
||||
};
|
||||
|
||||
#endif // SHARE_INTERPRETER_BYTECODETRACER_HPP
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -135,9 +135,6 @@ void interpreter_init_stub() {
|
||||
|
||||
void interpreter_init_code() {
|
||||
Interpreter::initialize_code();
|
||||
#ifndef PRODUCT
|
||||
if (TraceBytecodes) BytecodeTracer::set_closure(BytecodeTracer::std_closure());
|
||||
#endif // PRODUCT
|
||||
// need to hit every safepoint in order to call zapping routine
|
||||
// register the interpreter
|
||||
Forte::register_stub(
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "compiler/disassembler.hpp"
|
||||
#include "gc/shared/barrierSetNMethod.hpp"
|
||||
#include "gc/shared/collectedHeap.hpp"
|
||||
#include "interpreter/bytecodeTracer.hpp"
|
||||
#include "interpreter/interpreter.hpp"
|
||||
#include "interpreter/interpreterRuntime.hpp"
|
||||
#include "interpreter/linkResolver.hpp"
|
||||
@ -1509,7 +1510,7 @@ JRT_LEAF(intptr_t, InterpreterRuntime::trace_bytecode(JavaThread* current, intpt
|
||||
LastFrameAccessor last_frame(current);
|
||||
assert(last_frame.is_interpreted_frame(), "must be an interpreted frame");
|
||||
methodHandle mh(current, last_frame.method());
|
||||
BytecodeTracer::trace(mh, last_frame.bcp(), tos, tos2);
|
||||
BytecodeTracer::trace_interpreter(mh, last_frame.bcp(), tos, tos2);
|
||||
return preserve_this_value;
|
||||
JRT_END
|
||||
#endif // !PRODUCT
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -40,9 +40,6 @@ class CodeBuffer;
|
||||
// that cannot/should not be dealt with in assembly and needs C support.
|
||||
|
||||
class InterpreterRuntime: AllStatic {
|
||||
friend class BytecodeClosure; // for method and bcp
|
||||
friend class PrintingClosure; // for method and bcp
|
||||
|
||||
private:
|
||||
|
||||
static void set_bcp_and_mdp(address bcp, JavaThread* current);
|
||||
|
@ -446,7 +446,7 @@ void ConstantPoolCacheEntry::set_method_handle_common(const constantPoolHandle&
|
||||
NOT_PRODUCT(verify(tty));
|
||||
|
||||
if (log_stream != NULL) {
|
||||
this->print(log_stream, 0);
|
||||
this->print(log_stream, 0, cpool->cache());
|
||||
}
|
||||
|
||||
assert(has_appendix == this->has_appendix(), "proper storage of appendix flag");
|
||||
@ -480,7 +480,7 @@ bool ConstantPoolCacheEntry::save_and_throw_indy_exc(
|
||||
return true;
|
||||
}
|
||||
|
||||
Method* ConstantPoolCacheEntry::method_if_resolved(const constantPoolHandle& cpool) {
|
||||
Method* ConstantPoolCacheEntry::method_if_resolved(const constantPoolHandle& cpool) const {
|
||||
// Decode the action of set_method and set_interface_call
|
||||
Bytecodes::Code invoke_code = bytecode_1();
|
||||
if (invoke_code != (Bytecodes::Code)0) {
|
||||
@ -527,7 +527,7 @@ Method* ConstantPoolCacheEntry::method_if_resolved(const constantPoolHandle& cpo
|
||||
}
|
||||
|
||||
|
||||
oop ConstantPoolCacheEntry::appendix_if_resolved(const constantPoolHandle& cpool) {
|
||||
oop ConstantPoolCacheEntry::appendix_if_resolved(const constantPoolHandle& cpool) const {
|
||||
if (!has_appendix())
|
||||
return NULL;
|
||||
const int ref_index = f2_as_index();
|
||||
@ -620,7 +620,7 @@ Method* ConstantPoolCacheEntry::get_interesting_method_entry() {
|
||||
}
|
||||
#endif // INCLUDE_JVMTI
|
||||
|
||||
void ConstantPoolCacheEntry::print(outputStream* st, int index) const {
|
||||
void ConstantPoolCacheEntry::print(outputStream* st, int index, const ConstantPoolCache* cache) const {
|
||||
// print separator
|
||||
if (index == 0) st->print_cr(" -------------");
|
||||
// print entry
|
||||
@ -630,6 +630,25 @@ void ConstantPoolCacheEntry::print(outputStream* st, int index) const {
|
||||
st->print_cr(" [ " PTR_FORMAT "]", (intptr_t)_f1);
|
||||
st->print_cr(" [ " PTR_FORMAT "]", (intptr_t)_f2);
|
||||
st->print_cr(" [ " PTR_FORMAT "]", (intptr_t)_flags);
|
||||
|
||||
if ((bytecode_1() == Bytecodes::_invokehandle ||
|
||||
bytecode_1() == Bytecodes::_invokedynamic)) {
|
||||
constantPoolHandle cph(Thread::current(), cache->constant_pool());
|
||||
Method* m = method_if_resolved(cph);
|
||||
oop appendix = appendix_if_resolved(cph);
|
||||
ResourceMark rm;
|
||||
if (m != NULL) {
|
||||
st->print_cr(" Method%s: " INTPTR_FORMAT " %s.%s%s",
|
||||
m->is_native() ? " (native)" : "",
|
||||
p2i(m),
|
||||
m->method_holder()->name()->as_C_string(),
|
||||
m->name()->as_C_string(), m->signature()->as_C_string());
|
||||
}
|
||||
if (appendix != NULL) {
|
||||
st->print(" appendix: ");
|
||||
appendix->print_on(st);
|
||||
}
|
||||
}
|
||||
st->print_cr(" -------------");
|
||||
}
|
||||
|
||||
@ -784,7 +803,7 @@ bool ConstantPoolCache::check_no_old_or_obsolete_entries() {
|
||||
void ConstantPoolCache::dump_cache() {
|
||||
for (int i = 1; i < length(); i++) {
|
||||
if (entry_at(i)->get_interesting_method_entry() != NULL) {
|
||||
entry_at(i)->print(tty, i);
|
||||
entry_at(i)->print(tty, i, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -801,7 +820,7 @@ void ConstantPoolCache::metaspace_pointers_do(MetaspaceClosure* it) {
|
||||
void ConstantPoolCache::print_on(outputStream* st) const {
|
||||
st->print_cr("%s", internal_name());
|
||||
// print constant pool cache entries
|
||||
for (int i = 0; i < length(); i++) entry_at(i)->print(st, i);
|
||||
for (int i = 0; i < length(); i++) entry_at(i)->print(st, i, this);
|
||||
}
|
||||
|
||||
void ConstantPoolCache::print_value_on(outputStream* st) const {
|
||||
|
@ -292,8 +292,8 @@ class ConstantPoolCacheEntry {
|
||||
|
||||
// invokedynamic and invokehandle call sites have an "appendix" item in the
|
||||
// resolved references array.
|
||||
Method* method_if_resolved(const constantPoolHandle& cpool);
|
||||
oop appendix_if_resolved(const constantPoolHandle& cpool);
|
||||
Method* method_if_resolved(const constantPoolHandle& cpool) const;
|
||||
oop appendix_if_resolved(const constantPoolHandle& cpool) const;
|
||||
|
||||
void set_parameter_size(int value);
|
||||
|
||||
@ -379,7 +379,7 @@ class ConstantPoolCacheEntry {
|
||||
#endif // INCLUDE_JVMTI
|
||||
|
||||
// Debugging & Printing
|
||||
void print (outputStream* st, int index) const;
|
||||
void print (outputStream* st, int index, const ConstantPoolCache* cache) const;
|
||||
void verify(outputStream* st) const;
|
||||
|
||||
static void verify_tos_state_shift() {
|
||||
|
@ -1811,18 +1811,15 @@ void Method::print_name(outputStream* st) const {
|
||||
#endif // !PRODUCT || INCLUDE_JVMTI
|
||||
|
||||
|
||||
void Method::print_codes_on(outputStream* st) const {
|
||||
print_codes_on(0, code_size(), st);
|
||||
void Method::print_codes_on(outputStream* st, int flags) const {
|
||||
print_codes_on(0, code_size(), st, flags);
|
||||
}
|
||||
|
||||
void Method::print_codes_on(int from, int to, outputStream* st) const {
|
||||
void Method::print_codes_on(int from, int to, outputStream* st, int flags) const {
|
||||
Thread *thread = Thread::current();
|
||||
ResourceMark rm(thread);
|
||||
methodHandle mh (thread, (Method*)this);
|
||||
BytecodeStream s(mh);
|
||||
s.set_interval(from, to);
|
||||
BytecodeTracer::set_closure(BytecodeTracer::std_closure());
|
||||
while (s.next() >= 0) BytecodeTracer::trace(mh, s.bcp(), st);
|
||||
BytecodeTracer::print_method_codes(mh, from, to, st, flags);
|
||||
}
|
||||
|
||||
CompressedLineNumberReadStream::CompressedLineNumberReadStream(u_char* buffer) : CompressedReadStream(buffer) {
|
||||
|
@ -528,9 +528,9 @@ public:
|
||||
bool contains(address bcp) const { return constMethod()->contains(bcp); }
|
||||
|
||||
// prints byte codes
|
||||
void print_codes() const { print_codes_on(tty); }
|
||||
void print_codes_on(outputStream* st) const;
|
||||
void print_codes_on(int from, int to, outputStream* st) const;
|
||||
void print_codes(int flags = 0) const { print_codes_on(tty, flags); }
|
||||
void print_codes_on(outputStream* st, int flags = 0) const;
|
||||
void print_codes_on(int from, int to, outputStream* st, int flags = 0) const;
|
||||
|
||||
// method parameters
|
||||
bool has_method_parameters() const
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "runtime/signature.hpp"
|
||||
#include "utilities/stringUtils.hpp"
|
||||
#include "utilities/utf8.hpp"
|
||||
|
||||
Symbol* Symbol::_vm_symbols[vmSymbols::number_of_symbols()];
|
||||
@ -143,6 +144,15 @@ int Symbol::index_of_at(int i, const char* substr, int substr_len) const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Symbol::is_star_match(const char* pattern) const {
|
||||
if (strchr(pattern, '*') == NULL) {
|
||||
return equals(pattern);
|
||||
} else {
|
||||
ResourceMark rm;
|
||||
char* buf = as_C_string();
|
||||
return StringUtils::is_star_match(pattern, buf);
|
||||
}
|
||||
}
|
||||
|
||||
char* Symbol::as_C_string(char* buf, int size) const {
|
||||
if (size > 0) {
|
||||
|
@ -202,6 +202,7 @@ class Symbol : public MetaspaceObj {
|
||||
return contains_utf8_at(0, str, len);
|
||||
}
|
||||
bool equals(const char* str) const { return equals(str, (int) strlen(str)); }
|
||||
bool is_star_match(const char* pattern) const;
|
||||
|
||||
// Tests if the symbol starts with the given prefix.
|
||||
bool starts_with(const char* prefix, int len) const {
|
||||
|
@ -27,7 +27,6 @@
|
||||
|
||||
#include "code/codeBlob.hpp"
|
||||
#include "code/vmreg.hpp"
|
||||
#include "interpreter/bytecodeTracer.hpp"
|
||||
#include "interpreter/linkResolver.hpp"
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "jvm.h"
|
||||
#include "classfile/classPrinter.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "code/codeCache.hpp"
|
||||
#include "code/icBuffer.hpp"
|
||||
@ -637,8 +638,29 @@ extern "C" JNIEXPORT void findpc(intptr_t x) {
|
||||
os::print_location(tty, x, true);
|
||||
}
|
||||
|
||||
// For findmethod() and findclass():
|
||||
// - The patterns are matched by StringUtils::is_star_match()
|
||||
// - class_name_pattern matches Klass::external_name(). E.g., "java/lang/Object" or "*ang/Object"
|
||||
// - method_pattern may optionally the signature. E.g., "wait", "wait:()V" or "*ai*t:(*)V"
|
||||
// - flags must be OR'ed from ClassPrinter::Mode for findclass/findmethod
|
||||
// Examples (in gdb):
|
||||
// call findclass("java/lang/Object", 0x3) -> find j.l.Object and disasm all of its methods
|
||||
// call findmethod("*ang/Object*", "wait", 0xff) -> detailed disasm of all "wait" methods in j.l.Object
|
||||
// call findmethod("*ang/Object*", "wait:(*J*)V", 0x1) -> list all "wait" methods in j.l.Object that have a long parameter
|
||||
extern "C" JNIEXPORT void findclass(const char* class_name_pattern, int flags) {
|
||||
Command c("findclass");
|
||||
ClassPrinter::print_flags_help(tty);
|
||||
ClassPrinter::print_classes(class_name_pattern, flags, tty);
|
||||
}
|
||||
|
||||
// Need method pointer to find bcp, when not in permgen.
|
||||
extern "C" JNIEXPORT void findmethod(const char* class_name_pattern,
|
||||
const char* method_pattern, int flags) {
|
||||
Command c("findmethod");
|
||||
ClassPrinter::print_flags_help(tty);
|
||||
ClassPrinter::print_methods(class_name_pattern, method_pattern, flags, tty);
|
||||
}
|
||||
|
||||
// Need method pointer to find bcp
|
||||
extern "C" JNIEXPORT void findbcp(intptr_t method, intptr_t bcp) {
|
||||
Command c("findbcp");
|
||||
Method* mh = (Method*)method;
|
||||
@ -702,6 +724,9 @@ void help() {
|
||||
tty->print_cr(" pns($sp, $s8, $pc) on Linux/mips or");
|
||||
tty->print_cr(" - in gdb do 'set overload-resolution off' before calling pns()");
|
||||
tty->print_cr(" - in dbx do 'frame 1' before calling pns()");
|
||||
tty->print_cr("class metadata.");
|
||||
tty->print_cr(" findclass(name_pattern, flags)");
|
||||
tty->print_cr(" findmethod(class_name_pattern, method_pattern, flags)");
|
||||
|
||||
tty->print_cr("misc.");
|
||||
tty->print_cr(" flush() - flushes the log file");
|
||||
|
66
test/hotspot/gtest/runtime/test_classPrinter.cpp
Normal file
66
test/hotspot/gtest/runtime/test_classPrinter.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/classPrinter.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
#include "unittest.hpp"
|
||||
|
||||
TEST_VM(ClassPrinter, print_classes) {
|
||||
JavaThread* THREAD = JavaThread::current();
|
||||
ThreadInVMfromNative invm(THREAD);
|
||||
ResourceMark rm;
|
||||
|
||||
stringStream ss;
|
||||
ClassPrinter::print_classes("java/lang/Object", 0x03, &ss);
|
||||
const char* output = ss.freeze();
|
||||
|
||||
ASSERT_TRUE(strstr(output, "class java/lang/Object loader data:") != NULL) << "must find java/lang/Object";
|
||||
ASSERT_TRUE(strstr(output, "method wait : (J)V") != NULL) << "must find java/lang/Object::wait";
|
||||
ASSERT_TRUE(strstr(output, "method finalize : ()V\n 0 return") != NULL) << "must find java/lang/Object::finalize and disasm";
|
||||
}
|
||||
|
||||
TEST_VM(ClassPrinter, print_methods) {
|
||||
JavaThread* THREAD = JavaThread::current();
|
||||
ThreadInVMfromNative invm(THREAD);
|
||||
ResourceMark rm;
|
||||
|
||||
stringStream s1;
|
||||
ClassPrinter::print_methods("*ang/Object*", "wait", 0x1, &s1);
|
||||
const char* o1 = s1.freeze();
|
||||
ASSERT_TRUE(strstr(o1, "class java/lang/Object loader data:") != NULL) << "must find java/lang/Object";
|
||||
ASSERT_TRUE(strstr(o1, "method wait : (J)V") != NULL) << "must find java/lang/Object::wait(long)";
|
||||
ASSERT_TRUE(strstr(o1, "method wait : ()V") != NULL) << "must find java/lang/Object::wait()";
|
||||
ASSERT_TRUE(strstr(o1, "method finalize : ()V") == NULL) << "must not find java/lang/Object::finalize";
|
||||
|
||||
stringStream s2;
|
||||
ClassPrinter::print_methods("j*ang/Object*", "wait:(*J*)V", 0x1, &s2);
|
||||
const char* o2 = s2.freeze();
|
||||
ASSERT_TRUE(strstr(o2, "class java/lang/Object loader data:") != NULL) << "must find java/lang/Object";
|
||||
ASSERT_TRUE(strstr(o2, "method wait : (J)V") != NULL) << "must find java/lang/Object::wait(long)";
|
||||
ASSERT_TRUE(strstr(o2, "method wait : (JI)V") != NULL) << "must find java/lang/Object::wait(long,int)";
|
||||
ASSERT_TRUE(strstr(o2, "method wait : ()V") == NULL) << "must not find java/lang/Object::wait()";
|
||||
}
|
Loading…
Reference in New Issue
Block a user