Path: blob/master/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java
41153 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.instrument;2627import java.lang.instrument.UnmodifiableModuleException;28import java.lang.reflect.Method;29import java.lang.reflect.Modifier;30import java.lang.reflect.AccessibleObject;31import java.lang.instrument.ClassFileTransformer;32import java.lang.instrument.ClassDefinition;33import java.lang.instrument.Instrumentation;34import java.security.AccessController;35import java.security.PrivilegedAction;36import java.security.ProtectionDomain;37import java.util.Collections;38import java.util.ArrayList;39import java.util.HashMap;40import java.util.HashSet;41import java.util.List;42import java.util.Map;43import java.util.Set;44import java.util.jar.JarFile;4546import jdk.internal.module.Modules;47import jdk.internal.vm.annotation.IntrinsicCandidate;4849/*50* Copyright 2003 Wily Technology, Inc.51*/5253/**54* The Java side of the JPLIS implementation. Works in concert with a native JVMTI agent55* to implement the JPLIS API set. Provides both the Java API implementation of56* the Instrumentation interface and utility Java routines to support the native code.57* Keeps a pointer to the native data structure in a scalar field to allow native58* processing behind native methods.59*/60public class InstrumentationImpl implements Instrumentation {61private final TransformerManager mTransformerManager;62private TransformerManager mRetransfomableTransformerManager;63// needs to store a native pointer, so use 64 bits64private final long mNativeAgent;65private final boolean mEnvironmentSupportsRedefineClasses;66private volatile boolean mEnvironmentSupportsRetransformClassesKnown;67private volatile boolean mEnvironmentSupportsRetransformClasses;68private final boolean mEnvironmentSupportsNativeMethodPrefix;6970private71InstrumentationImpl(long nativeAgent,72boolean environmentSupportsRedefineClasses,73boolean environmentSupportsNativeMethodPrefix) {74mTransformerManager = new TransformerManager(false);75mRetransfomableTransformerManager = null;76mNativeAgent = nativeAgent;77mEnvironmentSupportsRedefineClasses = environmentSupportsRedefineClasses;78mEnvironmentSupportsRetransformClassesKnown = false; // false = need to ask79mEnvironmentSupportsRetransformClasses = false; // don't know yet80mEnvironmentSupportsNativeMethodPrefix = environmentSupportsNativeMethodPrefix;81}8283public void84addTransformer(ClassFileTransformer transformer) {85addTransformer(transformer, false);86}8788public synchronized void89addTransformer(ClassFileTransformer transformer, boolean canRetransform) {90if (transformer == null) {91throw new NullPointerException("null passed as 'transformer' in addTransformer");92}93if (canRetransform) {94if (!isRetransformClassesSupported()) {95throw new UnsupportedOperationException(96"adding retransformable transformers is not supported in this environment");97}98if (mRetransfomableTransformerManager == null) {99mRetransfomableTransformerManager = new TransformerManager(true);100}101mRetransfomableTransformerManager.addTransformer(transformer);102if (mRetransfomableTransformerManager.getTransformerCount() == 1) {103setHasRetransformableTransformers(mNativeAgent, true);104}105} else {106mTransformerManager.addTransformer(transformer);107if (mTransformerManager.getTransformerCount() == 1) {108setHasTransformers(mNativeAgent, true);109}110}111}112113public synchronized boolean114removeTransformer(ClassFileTransformer transformer) {115if (transformer == null) {116throw new NullPointerException("null passed as 'transformer' in removeTransformer");117}118TransformerManager mgr = findTransformerManager(transformer);119if (mgr != null) {120mgr.removeTransformer(transformer);121if (mgr.getTransformerCount() == 0) {122if (mgr.isRetransformable()) {123setHasRetransformableTransformers(mNativeAgent, false);124} else {125setHasTransformers(mNativeAgent, false);126}127}128return true;129}130return false;131}132133public boolean134isModifiableClass(Class<?> theClass) {135if (theClass == null) {136throw new NullPointerException(137"null passed as 'theClass' in isModifiableClass");138}139return isModifiableClass0(mNativeAgent, theClass);140}141142public boolean isModifiableModule(Module module) {143if (module == null) {144throw new NullPointerException("'module' is null");145}146return true;147}148149public boolean150isRetransformClassesSupported() {151// ask lazily since there is some overhead152if (!mEnvironmentSupportsRetransformClassesKnown) {153mEnvironmentSupportsRetransformClasses = isRetransformClassesSupported0(mNativeAgent);154mEnvironmentSupportsRetransformClassesKnown = true;155}156return mEnvironmentSupportsRetransformClasses;157}158159public void160retransformClasses(Class<?>... classes) {161if (!isRetransformClassesSupported()) {162throw new UnsupportedOperationException(163"retransformClasses is not supported in this environment");164}165if (classes.length == 0) {166return; // no-op167}168retransformClasses0(mNativeAgent, classes);169}170171public boolean172isRedefineClassesSupported() {173return mEnvironmentSupportsRedefineClasses;174}175176public void177redefineClasses(ClassDefinition... definitions)178throws ClassNotFoundException {179if (!isRedefineClassesSupported()) {180throw new UnsupportedOperationException("redefineClasses is not supported in this environment");181}182if (definitions == null) {183throw new NullPointerException("null passed as 'definitions' in redefineClasses");184}185for (int i = 0; i < definitions.length; ++i) {186if (definitions[i] == null) {187throw new NullPointerException("element of 'definitions' is null in redefineClasses");188}189}190if (definitions.length == 0) {191return; // short-circuit if there are no changes requested192}193194redefineClasses0(mNativeAgent, definitions);195}196197@SuppressWarnings("rawtypes")198public Class[]199getAllLoadedClasses() {200return getAllLoadedClasses0(mNativeAgent);201}202203@SuppressWarnings("rawtypes")204public Class[]205getInitiatedClasses(ClassLoader loader) {206return getInitiatedClasses0(mNativeAgent, loader);207}208209public long210getObjectSize(Object objectToSize) {211if (objectToSize == null) {212throw new NullPointerException("null passed as 'objectToSize' in getObjectSize");213}214return getObjectSize0(mNativeAgent, objectToSize);215}216217public void218appendToBootstrapClassLoaderSearch(JarFile jarfile) {219appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), true);220}221222public void223appendToSystemClassLoaderSearch(JarFile jarfile) {224appendToClassLoaderSearch0(mNativeAgent, jarfile.getName(), false);225}226227public boolean228isNativeMethodPrefixSupported() {229return mEnvironmentSupportsNativeMethodPrefix;230}231232public synchronized void233setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {234if (!isNativeMethodPrefixSupported()) {235throw new UnsupportedOperationException(236"setNativeMethodPrefix is not supported in this environment");237}238if (transformer == null) {239throw new NullPointerException(240"null passed as 'transformer' in setNativeMethodPrefix");241}242TransformerManager mgr = findTransformerManager(transformer);243if (mgr == null) {244throw new IllegalArgumentException(245"transformer not registered in setNativeMethodPrefix");246}247mgr.setNativeMethodPrefix(transformer, prefix);248String[] prefixes = mgr.getNativeMethodPrefixes();249setNativeMethodPrefixes(mNativeAgent, prefixes, mgr.isRetransformable());250}251252@Override253public void redefineModule(Module module,254Set<Module> extraReads,255Map<String, Set<Module>> extraExports,256Map<String, Set<Module>> extraOpens,257Set<Class<?>> extraUses,258Map<Class<?>, List<Class<?>>> extraProvides)259{260if (!module.isNamed())261return;262263if (!isModifiableModule(module))264throw new UnmodifiableModuleException(module.getName());265266// copy and check reads267extraReads = new HashSet<>(extraReads);268if (extraReads.contains(null))269throw new NullPointerException("'extraReads' contains null");270271// copy and check exports and opens272extraExports = cloneAndCheckMap(module, extraExports);273extraOpens = cloneAndCheckMap(module, extraOpens);274275// copy and check uses276extraUses = new HashSet<>(extraUses);277if (extraUses.contains(null))278throw new NullPointerException("'extraUses' contains null");279280// copy and check provides281Map<Class<?>, List<Class<?>>> tmpProvides = new HashMap<>();282for (Map.Entry<Class<?>, List<Class<?>>> e : extraProvides.entrySet()) {283Class<?> service = e.getKey();284if (service == null)285throw new NullPointerException("'extraProvides' contains null");286List<Class<?>> providers = new ArrayList<>(e.getValue());287if (providers.isEmpty())288throw new IllegalArgumentException("list of providers is empty");289providers.forEach(p -> {290if (p.getModule() != module)291throw new IllegalArgumentException(p + " not in " + module);292if (!service.isAssignableFrom(p))293throw new IllegalArgumentException(p + " is not a " + service);294});295tmpProvides.put(service, providers);296}297extraProvides = tmpProvides;298299300// update reads301extraReads.forEach(m -> Modules.addReads(module, m));302303// update exports304for (Map.Entry<String, Set<Module>> e : extraExports.entrySet()) {305String pkg = e.getKey();306Set<Module> targets = e.getValue();307targets.forEach(m -> Modules.addExports(module, pkg, m));308}309310// update opens311for (Map.Entry<String, Set<Module>> e : extraOpens.entrySet()) {312String pkg = e.getKey();313Set<Module> targets = e.getValue();314targets.forEach(m -> Modules.addOpens(module, pkg, m));315}316317// update uses318extraUses.forEach(service -> Modules.addUses(module, service));319320// update provides321for (Map.Entry<Class<?>, List<Class<?>>> e : extraProvides.entrySet()) {322Class<?> service = e.getKey();323List<Class<?>> providers = e.getValue();324providers.forEach(p -> Modules.addProvides(module, service, p));325}326}327328private Map<String, Set<Module>>329cloneAndCheckMap(Module module, Map<String, Set<Module>> map)330{331if (map.isEmpty())332return Collections.emptyMap();333334Map<String, Set<Module>> result = new HashMap<>();335Set<String> packages = module.getPackages();336for (Map.Entry<String, Set<Module>> e : map.entrySet()) {337String pkg = e.getKey();338if (pkg == null)339throw new NullPointerException("package cannot be null");340if (!packages.contains(pkg))341throw new IllegalArgumentException(pkg + " not in module");342Set<Module> targets = new HashSet<>(e.getValue());343if (targets.isEmpty())344throw new IllegalArgumentException("set of targets is empty");345if (targets.contains(null))346throw new NullPointerException("set of targets cannot include null");347result.put(pkg, targets);348}349return result;350}351352353private TransformerManager354findTransformerManager(ClassFileTransformer transformer) {355if (mTransformerManager.includesTransformer(transformer)) {356return mTransformerManager;357}358if (mRetransfomableTransformerManager != null &&359mRetransfomableTransformerManager.includesTransformer(transformer)) {360return mRetransfomableTransformerManager;361}362return null;363}364365366/*367* Natives368*/369private native boolean370isModifiableClass0(long nativeAgent, Class<?> theClass);371372private native boolean373isRetransformClassesSupported0(long nativeAgent);374375private native void376setHasTransformers(long nativeAgent, boolean has);377378private native void379setHasRetransformableTransformers(long nativeAgent, boolean has);380381private native void382retransformClasses0(long nativeAgent, Class<?>[] classes);383384private native void385redefineClasses0(long nativeAgent, ClassDefinition[] definitions)386throws ClassNotFoundException;387388@SuppressWarnings("rawtypes")389private native Class[]390getAllLoadedClasses0(long nativeAgent);391392@SuppressWarnings("rawtypes")393private native Class[]394getInitiatedClasses0(long nativeAgent, ClassLoader loader);395396@IntrinsicCandidate397private native long398getObjectSize0(long nativeAgent, Object objectToSize);399400private native void401appendToClassLoaderSearch0(long nativeAgent, String jarfile, boolean bootLoader);402403private native void404setNativeMethodPrefixes(long nativeAgent, String[] prefixes, boolean isRetransformable);405406static {407System.loadLibrary("instrument");408}409410/*411* Internals412*/413414415// Enable or disable Java programming language access checks on a416// reflected object (for example, a method)417@SuppressWarnings("removal")418private static void setAccessible(final AccessibleObject ao, final boolean accessible) {419AccessController.doPrivileged(new PrivilegedAction<Object>() {420public Object run() {421ao.setAccessible(accessible);422return null;423}});424}425426// Attempt to load and start an agent427private void428loadClassAndStartAgent( String classname,429String methodname,430String optionsString)431throws Throwable {432433ClassLoader mainAppLoader = ClassLoader.getSystemClassLoader();434Class<?> javaAgentClass = mainAppLoader.loadClass(classname);435436Method m = null;437NoSuchMethodException firstExc = null;438boolean twoArgAgent = false;439440// The agent class must have a premain or agentmain method that441// has 1 or 2 arguments. We check in the following order:442//443// 1) declared with a signature of (String, Instrumentation)444// 2) declared with a signature of (String)445//446// If no method is found then we throw the NoSuchMethodException447// from the first attempt so that the exception text indicates448// the lookup failed for the 2-arg method (same as JDK5.0).449450try {451m = javaAgentClass.getDeclaredMethod( methodname,452new Class<?>[] {453String.class,454java.lang.instrument.Instrumentation.class455}456);457twoArgAgent = true;458} catch (NoSuchMethodException x) {459// remember the NoSuchMethodException460firstExc = x;461}462463if (m == null) {464// now try the declared 1-arg method465try {466m = javaAgentClass.getDeclaredMethod(methodname,467new Class<?>[] { String.class });468} catch (NoSuchMethodException x) {469// none of the methods exists so we throw the470// first NoSuchMethodException as per 5.0471throw firstExc;472}473}474475// reject non-public premain or agentmain method476if (!Modifier.isPublic(m.getModifiers())) {477String msg = "method " + classname + "." + methodname + " must be declared public";478throw new IllegalAccessException(msg);479}480481if (!Modifier.isPublic(javaAgentClass.getModifiers()) &&482!javaAgentClass.getModule().isNamed()) {483// If the java agent class is in an unnamed module, the java agent class can be non-public.484// Suppress access check upon the invocation of the premain/agentmain method.485setAccessible(m, true);486}487488// invoke the 1 or 2-arg method489if (twoArgAgent) {490m.invoke(null, new Object[] { optionsString, this });491} else {492m.invoke(null, new Object[] { optionsString });493}494}495496// WARNING: the native code knows the name & signature of this method497private void498loadClassAndCallPremain( String classname,499String optionsString)500throws Throwable {501502loadClassAndStartAgent( classname, "premain", optionsString );503}504505506// WARNING: the native code knows the name & signature of this method507private void508loadClassAndCallAgentmain( String classname,509String optionsString)510throws Throwable {511512loadClassAndStartAgent( classname, "agentmain", optionsString );513}514515// WARNING: the native code knows the name & signature of this method516private byte[]517transform( Module module,518ClassLoader loader,519String classname,520Class<?> classBeingRedefined,521ProtectionDomain protectionDomain,522byte[] classfileBuffer,523boolean isRetransformer) {524TransformerManager mgr = isRetransformer?525mRetransfomableTransformerManager :526mTransformerManager;527// module is null when not a class load or when loading a class in an528// unnamed module and this is the first type to be loaded in the package.529if (module == null) {530if (classBeingRedefined != null) {531module = classBeingRedefined.getModule();532} else {533module = (loader == null) ? jdk.internal.loader.BootLoader.getUnnamedModule()534: loader.getUnnamedModule();535}536}537if (mgr == null) {538return null; // no manager, no transform539} else {540return mgr.transform( module,541loader,542classname,543classBeingRedefined,544protectionDomain,545classfileBuffer);546}547}548549550/**551* Invoked by the java launcher to load a java agent that is packaged with552* the main application in an executable JAR file.553*/554public static void loadAgent(String path) {555loadAgent0(path);556}557558private static native void loadAgent0(String path);559}560561562