8272065: jcmd cannot rely on the old core reflection implementation which will be changed after JEP 416

Reviewed-by: mchung, coleenp, dholmes
This commit is contained in:
Thomas Stuefe 2021-11-05 05:15:53 +00:00
parent 8e17ce0031
commit 7281861e06
8 changed files with 1 additions and 368 deletions

View File

@ -31,7 +31,6 @@
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "runtime/safepoint.hpp" #include "runtime/safepoint.hpp"
#include "oops/reflectionAccessorImplKlassHelper.hpp"
#include "utilities/globalDefinitions.hpp" #include "utilities/globalDefinitions.hpp"
#include "utilities/ostream.hpp" #include "utilities/ostream.hpp"
@ -172,7 +171,7 @@ class LoaderTreeNode : public ResourceObj {
branchtracker.print(st); branchtracker.print(st);
// e.g. "+--- jdk.internal.reflect.DelegatingClassLoader" // e.g. +-- "app", jdk.internal.loader.ClassLoaders$AppClassLoader
st->print("+%.*s", BranchTracker::twig_len, "----------"); st->print("+%.*s", BranchTracker::twig_len, "----------");
if (_cld->is_the_null_class_loader_data()) { if (_cld->is_the_null_class_loader_data()) {
st->print(" <bootstrap>"); st->print(" <bootstrap>");
@ -232,14 +231,6 @@ class LoaderTreeNode : public ResourceObj {
st->print("%*s ", indentation, ""); st->print("%*s ", indentation, "");
} }
st->print("%s", lci->_klass->external_name()); st->print("%s", lci->_klass->external_name());
// Special treatment for generated core reflection accessor classes: print invocation target.
if (ReflectionAccessorImplKlassHelper::is_generated_accessor(lci->_klass)) {
st->print(" (invokes: ");
ReflectionAccessorImplKlassHelper::print_invocation_target(st, lci->_klass);
st->print(")");
}
st->cr(); st->cr();
} }
branchtracker.print(st); branchtracker.print(st);

View File

@ -34,7 +34,6 @@
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "memory/universe.hpp" #include "memory/universe.hpp"
#include "oops/oop.inline.hpp" #include "oops/oop.inline.hpp"
#include "oops/reflectionAccessorImplKlassHelper.hpp"
#include "runtime/atomic.hpp" #include "runtime/atomic.hpp"
#include "runtime/os.hpp" #include "runtime/os.hpp"
#include "services/memTracker.hpp" #include "services/memTracker.hpp"
@ -474,12 +473,6 @@ void KlassHierarchy::print_class(outputStream* st, KlassInfoEntry* cie, bool pri
if (klass->is_interface()) { if (klass->is_interface()) {
st->print(" (intf)"); st->print(" (intf)");
} }
// Special treatment for generated core reflection accessor classes: print invocation target.
if (ReflectionAccessorImplKlassHelper::is_generated_accessor(klass)) {
st->print(" (invokes: ");
ReflectionAccessorImplKlassHelper::print_invocation_target(st, klass);
st->print(")");
}
st->print("\n"); st->print("\n");
// Print any interfaces the class has. // Print any interfaces the class has.

View File

@ -26,7 +26,6 @@
#include "memory/metaspace/printMetaspaceInfoKlassClosure.hpp" #include "memory/metaspace/printMetaspaceInfoKlassClosure.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "oops/klass.hpp" #include "oops/klass.hpp"
#include "oops/reflectionAccessorImplKlassHelper.hpp"
#include "utilities/globalDefinitions.hpp" #include "utilities/globalDefinitions.hpp"
#include "utilities/ostream.hpp" #include "utilities/ostream.hpp"
@ -46,13 +45,6 @@ void PrintMetaspaceInfoKlassClosure::do_klass(Klass* k) {
ResourceMark rm; ResourceMark rm;
_out->print(" %s", k->external_name()); _out->print(" %s", k->external_name());
// Special treatment for generated core reflection accessor classes: print invocation target.
if (ReflectionAccessorImplKlassHelper::is_generated_accessor(k)) {
_out->print(" (invokes: ");
ReflectionAccessorImplKlassHelper::print_invocation_target(_out, k);
_out->print(")");
}
} }
} // namespace metaspace } // namespace metaspace

View File

@ -1,165 +0,0 @@
/*
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 SAP SE. 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/vmClasses.hpp"
#include "memory/resourceArea.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/constantPool.hpp"
#include "oops/reflectionAccessorImplKlassHelper.hpp"
#include "utilities/constantTag.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
// This code extracts name of target class, method and signature from the constant pool of a class
// assumed to be of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX.
// Since this may be affected by bitrot if these classes change, extra care is taken to make the
// release build of this coding robust.
// We extract target class name, method name and sig from the constant pool of the Accessor class.
// This is an excerpt of the Constant pool (see jdk/internal/reflect/MethodAccessorGenerator.java:)
// (^ = Only present if generating SerializationConstructorAccessor)
// 1 [UTF-8] [This class's name]
// 2 [CONSTANT_Class_info] for above
// 3 [UTF-8] "jdk/internal/reflect/{MethodAccessorImpl,ConstructorAccessorImpl,SerializationConstructorAccessorImpl}"
// 4 [CONSTANT_Class_info] for above
// 5 [UTF-8] [Target class's name]
// 6 [CONSTANT_Class_info] for above
// 7^ [UTF-8] [Serialization: Class's name in which to invoke constructor]
// 8^ [CONSTANT_Class_info] for above
// 9 [UTF-8] target method or constructor name
// 10 [UTF-8] target method or constructor signature
// Note that these strings are found at slightly different slots depending on the class type:
// - MethodAccessorImpl, ConstructoreAccessorImpl: slots 5, 7 and 8.
// - SerializationConstructorAccessorImpl: slots 5, 9 and 10.
// Unfortunately SerializationConstructorAccessorImpl is a child of ConstructoreAccessorImpl and there
// is no easy way to tell them apart. So we examine parent class name.
enum cpi_slots {
cpi_slot_parent_class_name = 3,
cpi_slot_target_class_name = 5,
cpi_slot_target_method_name = 7,
cpi_slot_target_method_name_sca = 9, // SerializationConstructorAccessor case, see above
cpi_slot_target_method_sig = 8,
cpi_slot_target_method_sig_sca = 10 // SerializationConstructorAccessor case, see above
};
// Returns a string, resource-area allocated, from an UTF8 slot in the constant pool in the
// given Klass*.
static const char* get_string_from_cp_with_checks(const InstanceKlass* k, int cpi) {
const char* s = NULL;
const ConstantPool* const cp = k->constants();
assert(cp != NULL, "No cp?");
assert(cp->is_within_bounds(cpi), "Unexpected constant pool layout for \"%s\", child class of Generated{Method|Constructor}AccessorImplXXX"
" (cpi %d out of bounds for [0..%d)).", k->external_name(), cpi, cp->length());
assert(cp->tag_at(cpi).is_utf8(), "Unexpected constant pool layout for \"%s\", child class of Generated{Method|Constructor}AccessorImplXXX"
" (no UTF8 at cpi %d (%u)).", k->external_name(), cpi, cp->tag_at(cpi).value());
// Be nice in release: lets not crash, just return NULL.
if (cp != NULL && cp->is_within_bounds(cpi) && cp->tag_at(cpi).is_utf8()) {
s = cp->symbol_at(cpi)->as_C_string();
}
return s;
}
// helper, returns true if class name of given class matches a given prefix
static bool classname_matches_prefix(const Klass* k, const char* prefix) {
const char* classname = k->external_name();
if (classname != NULL) {
if (::strncmp(classname, prefix, strlen(prefix)) == 0) {
return true;
}
}
return false;
}
// Returns true if k is of type jdk/internal/reflect/GeneratedMethodAccessorXXX.
bool ReflectionAccessorImplKlassHelper::is_generated_method_accessor(const InstanceKlass* k) {
return k->super() == vmClasses::reflect_MethodAccessorImpl_klass() &&
classname_matches_prefix(k, "jdk.internal.reflect.GeneratedMethodAccessor");
}
// Returns true if k is of type jdk/internal/reflect/GeneratedConstructorAccessorXXX.
bool ReflectionAccessorImplKlassHelper::is_generated_constructor_accessor(const InstanceKlass* k) {
return k->super() == vmClasses::reflect_ConstructorAccessorImpl_klass() &&
classname_matches_prefix(k, "jdk.internal.reflect.GeneratedConstructorAccessor");
}
// Returns true if k is of type jdk/internal/reflect/GeneratedSerializationConstructorAccessorXXX.
bool ReflectionAccessorImplKlassHelper::is_generated_method_serialization_constructor_accessor(const InstanceKlass* k) {
// GeneratedSerializationConstructorAccessor is not a direct subclass of ConstructorAccessorImpl
const Klass* sk = k->super();
if (sk != NULL && sk->super() == vmClasses::reflect_ConstructorAccessorImpl_klass() &&
classname_matches_prefix(k, "jdk.internal.reflect.GeneratedSerializationConstructorAccessor")) {
return true;
}
return false;
}
const char* ReflectionAccessorImplKlassHelper::get_target_class_name(const InstanceKlass* k) {
return get_string_from_cp_with_checks(k, cpi_slot_target_class_name);
}
const char* ReflectionAccessorImplKlassHelper::get_target_method_name(const InstanceKlass* k) {
const int target_method_name_cpi =
is_generated_method_serialization_constructor_accessor(k) ? cpi_slot_target_method_name_sca : cpi_slot_target_method_name;
return get_string_from_cp_with_checks(k, target_method_name_cpi);
}
const char* ReflectionAccessorImplKlassHelper::get_target_method_signature(const InstanceKlass* k) {
const int target_method_name_cpi =
is_generated_method_serialization_constructor_accessor(k) ? cpi_slot_target_method_sig_sca : cpi_slot_target_method_sig;
return get_string_from_cp_with_checks(k, target_method_name_cpi);
}
// Returns true if this is either one of jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX
// and it is safe to call print_invocation_target(k)
bool ReflectionAccessorImplKlassHelper::is_generated_accessor(const Klass* k) {
if (k != NULL && k->is_instance_klass()) {
const InstanceKlass* ik = InstanceKlass::cast(k);
if (ik->is_initialized()) {
return is_generated_method_accessor(ik) ||
is_generated_constructor_accessor(ik) ||
is_generated_method_serialization_constructor_accessor(ik);
}
}
return false;
}
void ReflectionAccessorImplKlassHelper::print_invocation_target(outputStream* out, Klass* k) {
assert(ReflectionAccessorImplKlassHelper::is_generated_accessor(k), "Invariant");
InstanceKlass* ik = InstanceKlass::cast(k);
ResourceMark rm;
const char* target_class_name = ReflectionAccessorImplKlassHelper::get_target_class_name(ik);
const char* target_method_name = ReflectionAccessorImplKlassHelper::get_target_method_name(ik);
const char* target_method_signature = ReflectionAccessorImplKlassHelper::get_target_method_signature(ik);
out->print("%s::%s %s",
target_class_name != NULL ? target_class_name : "?",
target_method_name != NULL ? target_method_name : "?",
target_method_signature != NULL ? target_method_signature : "?");
}

View File

@ -1,74 +0,0 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 SAP SE. 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_OOPS_REFLECTIONACCESSORIMPLKLASSHELPER_HPP
#define SHARE_OOPS_REFLECTIONACCESSORIMPLKLASSHELPER_HPP
#include "memory/allocation.hpp"
class InstanceKlass;
// Helper for classes derived from jdk/internal/reflect/{Method|Constructor}AccessorImpl:
// offers convenience functions to extract the names of target class/method/signature
// from the constant pool of these classes.
class ReflectionAccessorImplKlassHelper: public AllStatic {
// Returns true if k is of type jdk/internal/reflect/GeneratedMethodAccessorXXX.
static bool is_generated_method_accessor(const InstanceKlass* k);
// Returns true if k is of type jdk/internal/reflect/GeneratedConstructorAccessorXXX.
static bool is_generated_constructor_accessor(const InstanceKlass* k);
// Returns true if k is of type jdk/internal/reflect/GeneratedSerializationConstructorAccessorXXX.
static bool is_generated_method_serialization_constructor_accessor(const InstanceKlass* k);
// Assuming k is of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX,
// the name of the target class as resource-area allocated string.
static const char* get_target_class_name(const InstanceKlass* k);
// Assuming k is of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX,
// the name of the target method as resource-area allocated string.
static const char* get_target_method_name(const InstanceKlass* k);
// Assuming k is of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX,
// the signature of the target method as resource-area allocated string.
static const char* get_target_method_signature(const InstanceKlass* k);
public:
// Returns true if k is of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX
// and it is safe to call print_invocation_target(k)
static bool is_generated_accessor(const Klass* k);
// Assuming k is of type jdk/internal/reflect/Generated{SerializationConstructor|Constructor|Method}AccessorXXX,
// print out target class, method, signature in one line.
static void print_invocation_target(outputStream* out, Klass* k);
};
#endif // SHARE_OOPS_REFLECTIONACCESSORIMPLKLASSHELPER_HPP

View File

@ -53,10 +53,6 @@ public class ClassLoaderHierarchyTest {
// | | // | |
// | +-- "app", jdk.internal.loader.ClassLoaders$AppClassLoader // | +-- "app", jdk.internal.loader.ClassLoaders$AppClassLoader
// | // |
// +-- jdk.internal.reflect.DelegatingClassLoader
// |
// +-- "Kevin", ClassLoaderHierarchyTest$TestClassLoader
// |
// +-- ClassLoaderHierarchyTest$TestClassLoader // +-- ClassLoaderHierarchyTest$TestClassLoader
// | // |
// +-- "Bill", ClassLoaderHierarchyTest$TestClassLoader // +-- "Bill", ClassLoaderHierarchyTest$TestClassLoader

View File

@ -62,7 +62,6 @@ public class ClassLoaderStatsTest {
// 1 256 131 + hidden classes // 1 256 131 + hidden classes
// 0x0000000000000000 0x0000000000000000 0x00007f00e852d190 1607 4628480 3931216 <boot class loader> // 0x0000000000000000 0x0000000000000000 0x00007f00e852d190 1607 4628480 3931216 <boot class loader>
// 38 124928 85856 + hidden classes // 38 124928 85856 + hidden classes
// 0x00000008003b5508 0x0000000000000000 0x00007f001c2d4760 1 6144 4040 jdk.internal.reflect.DelegatingClassLoader
// 0x000000080037f468 0x000000080037ee80 0x00007f00e868e3f0 228 1368064 1286672 jdk.internal.loader.ClassLoaders$AppClassLoader // 0x000000080037f468 0x000000080037ee80 0x00007f00e868e3f0 228 1368064 1286672 jdk.internal.loader.ClassLoaders$AppClassLoader
// ... // ...

View File

@ -1,99 +0,0 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 SAP SE. 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.
*/
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.Assert;
import org.testng.annotations.Test;
import jdk.test.lib.dcmd.CommandExecutor;
import jdk.test.lib.dcmd.JMXExecutor;
import jdk.test.lib.process.OutputAnalyzer;
/*
* @test
* @summary Test that various diagnostic commands which can show core reflection
* invocation targets do so correctly (See: JDK-8203343).
* @library /test/lib
* @run testng/othervm -Dsun.reflect.noInflation=true -Djdk.reflect.useDirectMethodHandle=false ShowReflectionTargetTest
* @author stuefe
*/
public class ShowReflectionTargetTest {
@SuppressWarnings("unused")
private static class Dummy {
int _i;
public Dummy(int i) { _i = i; }
public int get_i() { return _i; }
}
public void run(CommandExecutor executor) throws Exception {
// Do some reflection; since we set -Dsun.reflect.noInflation=true, this should
// immediately generate Generated{Method|Constructor}Accessor objects.
Class<?> c = Class.forName("ShowReflectionTargetTest$Dummy");
Constructor<?> ctor = c.getConstructor(int.class);
Method m = c.getMethod("get_i");
Object o = ctor.newInstance(17);
int j = ((Integer)m.invoke(o)).intValue();
Assert.assertEquals(j, 17);
// Now invoke VM.class_hierarchy and check its output.
// Should show reflection targets, e.g.:
// ....
// |--jdk.internal.reflect.MagicAccessorImpl/null
// | |--jdk.internal.reflect.FieldAccessorImpl/null
// | | |--jdk.internal.reflect.UnsafeFieldAccessorImpl/null
// | | | |--jdk.internal.reflect.UnsafeStaticFieldAccessorImpl/null
// | | | | |--jdk.internal.reflect.UnsafeQualifiedStaticFieldAccessorImpl/null
// | | | | | |--jdk.internal.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl/null
// | |--jdk.internal.reflect.ConstructorAccessorImpl/null
// | | |--jdk.internal.reflect.DelegatingConstructorAccessorImpl/null
// | | |--jdk.internal.reflect.NativeConstructorAccessorImpl/null
// > | | |--jdk.internal.reflect.GeneratedConstructorAccessor1/0x00007f75f04889b0 (invokes: java/lang/management/ManagementPermission::<init> (Ljava/lang/String;)V)
// > | | |--jdk.internal.reflect.GeneratedConstructorAccessor2/0x00007f75f0494990 (invokes: ShowReflectionTargetTest$Dummy::<init> (I)V)
// | | |--jdk.internal.reflect.BootstrapConstructorAccessorImpl/null
// | |--jdk.internal.reflect.MethodAccessorImpl/null
// > | | |--jdk.internal.reflect.GeneratedMethodAccessor1/0x00007f75f0494450 (invokes: ShowReflectionTargetTest$Dummy::get_i ()I)
// | | |--jdk.internal.reflect.DelegatingMethodAccessorImpl/null
// ...
OutputAnalyzer output = executor.execute("VM.class_hierarchy");
output.shouldMatch(".*jdk.internal.reflect.GeneratedConstructorAccessor.*invokes.*ShowReflectionTargetTest\\$Dummy::<init>.*");
output.shouldMatch(".*jdk.internal.reflect.GeneratedMethodAccessor.*invokes.*ShowReflectionTargetTest\\$Dummy::get_i.*");
}
@Test
public void jmx() throws Exception {
run(new JMXExecutor());
}
}