8292699: Improve printing of classes in native debugger

Reviewed-by: coleenp
This commit is contained in:
Ioi Lam 2022-10-24 22:17:51 +00:00
parent 7520d0a725
commit 89dafc002f
16 changed files with 498 additions and 108 deletions

View 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);
}

View 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

View File

@ -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:

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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);

View File

@ -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 {

View File

@ -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() {

View File

@ -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) {

View File

@ -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

View File

@ -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) {

View File

@ -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 {

View File

@ -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"

View File

@ -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");

View 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()";
}