All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glowroot.instrumentation.engine.weaving.AdviceCache Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.glowroot.instrumentation.engine.weaving;

import java.io.File;
import java.lang.instrument.Instrumentation;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.glowroot.instrumentation.test.harness.shaded.com.google.common.annotations.VisibleForTesting;
import org.glowroot.instrumentation.test.harness.shaded.com.google.common.base.Supplier;
import org.glowroot.instrumentation.test.harness.shaded.com.google.common.collect.ImmutableList;
import org.glowroot.instrumentation.test.harness.shaded.com.google.common.collect.ImmutableMap;
import org.glowroot.instrumentation.test.harness.shaded.com.google.common.collect.ImmutableSet;
import org.glowroot.instrumentation.test.harness.shaded.com.google.common.collect.Iterables;
import org.glowroot.instrumentation.test.harness.shaded.com.google.common.collect.Lists;
import org.glowroot.instrumentation.test.harness.shaded.com.google.common.collect.Maps;
import org.glowroot.instrumentation.test.harness.shaded.com.google.common.collect.Sets;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.glowroot.instrumentation.test.harness.shaded.org.slf4j.Logger;
import org.glowroot.instrumentation.test.harness.shaded.org.slf4j.LoggerFactory;

import org.glowroot.instrumentation.engine.config.AdviceConfig;
import org.glowroot.instrumentation.engine.config.InstrumentationDescriptor;
import org.glowroot.instrumentation.engine.weaving.ClassLoaders.LazyDefinedClass;
import org.glowroot.instrumentation.engine.weaving.InstrumentationDetail.PointcutClass;
import org.glowroot.instrumentation.engine.weaving.Reweaving.PointcutClassName;

import static org.glowroot.instrumentation.test.harness.shaded.com.google.common.base.Preconditions.checkNotNull;

public class AdviceCache {

    private static final Logger logger = LoggerFactory.getLogger(AdviceCache.class);

    private final ImmutableList nonReweavableAdvisors;
    private final ImmutableList shimTypes;
    private final ImmutableList mixinTypes;
    private final @Nullable Instrumentation instrumentation;
    private final List doNotWeavePrefixes;
    private final File tmpDir;

    private volatile ImmutableSet reweavableAdviceConfigs;
    private volatile ImmutableList reweavableAdvisors;

    private volatile ImmutableList allAdvisors;

    public AdviceCache(List instrumentationDescriptors,
            List reweavableAdviceConfigs, @Nullable Instrumentation instrumentation,
            List doNotWeavePrefixes, File tmpDir) throws Exception {

        List nonReweavableAdvisors = Lists.newArrayList();
        List shimTypes = Lists.newArrayList();
        List mixinTypes = Lists.newArrayList();
        Map lazyAdvisors = Maps.newHashMap();
        for (InstrumentationDescriptor descriptor : instrumentationDescriptors) {
            InstrumentationDetailBuilder builder = new InstrumentationDetailBuilder(descriptor);
            InstrumentationDetail detail = builder.build();

            nonReweavableAdvisors.addAll(getAdvisors(detail.pointcutClasses()));
            mixinTypes.addAll(detail.mixinTypes());
            shimTypes.addAll(detail.shimTypes());

            List configs = descriptor.adviceConfigs();
            for (AdviceConfig config : configs) {
                config.logValidationErrorsIfAny();
            }
            lazyAdvisors.putAll(AdviceGenerator.createAdvisors(configs, descriptor.id(),
                    descriptor.jarFile() != null, false));
        }
        for (Map.Entry entry : lazyAdvisors.entrySet()) {
            nonReweavableAdvisors.add(entry.getKey());
        }
        if (instrumentation == null) {
            // instrumentation is null when debugging with LocalContainer
            ClassLoader isolatedWeavingClassLoader = Thread.currentThread().getContextClassLoader();
            checkNotNull(isolatedWeavingClassLoader);
            ClassLoaders.defineClasses(lazyAdvisors.values(), isolatedWeavingClassLoader);
        } else {
            ClassLoaders.createDirectoryOrCleanPreviousContentsWithPrefix(tmpDir,
                    "instrumentation-advice-");
            ClassLoaders.defineClassesInBootstrapClassLoader(lazyAdvisors.values(), instrumentation,
                    tmpDir, "instrumentation-advice-");
        }
        this.nonReweavableAdvisors = ImmutableList.copyOf(nonReweavableAdvisors);
        this.shimTypes = ImmutableList.copyOf(shimTypes);
        this.mixinTypes = ImmutableList.copyOf(mixinTypes);
        this.instrumentation = instrumentation;
        this.doNotWeavePrefixes = doNotWeavePrefixes;
        this.tmpDir = tmpDir;
        this.reweavableAdviceConfigs = ImmutableSet.copyOf(reweavableAdviceConfigs);
        reweavableAdvisors =
                createReweavableAdvisors(reweavableAdviceConfigs, instrumentation, tmpDir, true);
        allAdvisors = ImmutableList
                .copyOf(Iterables.concat(nonReweavableAdvisors, reweavableAdvisors));
    }

    public Supplier> getAdvisorsSupplier() {
        return new Supplier>() {
            @Override
            public List get() {
                return allAdvisors;
            }
        };
    }

    @VisibleForTesting
    public List getShimTypes() {
        return shimTypes;
    }

    @VisibleForTesting
    public List getMixinTypes() {
        return mixinTypes;
    }

    public void initialReweave(Class[] initialLoadedClasses) {
        Set pointcutClassNames = Sets.newHashSet();
        for (Advice advice : allAdvisors) {
            PointcutClassName pointcutClassName = getPointcutClassName(advice);
            // don't add Runnable/Callable subclasses to initial reweave, since they won't work
            // anyways since too late to add mixin interface
            // this is just an optimization, and (importantly) to keep class retransformation down
            // to a minimum since it has been known to have some problems on some JVMs
            if (pointcutClassName != null
                    && !advice.adviceType().getInternalName().startsWith("org/glowroot"
                            + "/instrumentation/executor/ExecutorInstrumentation$RunnableAdvice")
                    && !advice.adviceType().getInternalName().startsWith("org/glowroot"
                            + "/instrumentation/executor/ExecutorInstrumentation$CallableAdvice")) {
                pointcutClassNames.add(pointcutClassName);
            }
        }
        Reweaving.initialReweave(pointcutClassNames, initialLoadedClasses,
                checkNotNull(instrumentation), doNotWeavePrefixes);
    }

    public void updateAdvisors(List reweavableConfigs)
            throws Exception {
        reweavableAdvisors =
                createReweavableAdvisors(reweavableConfigs, instrumentation, tmpDir, false);
        this.reweavableAdviceConfigs = ImmutableSet.copyOf(reweavableConfigs);
        allAdvisors = ImmutableList
                .copyOf(Iterables.concat(nonReweavableAdvisors, reweavableAdvisors));
    }

    public boolean isOutOfSync(List reweavableAdviceConfigs) {
        return !this.reweavableAdviceConfigs
                .equals(ImmutableSet.copyOf(reweavableAdviceConfigs));
    }

    private static List getAdvisors(List adviceClasses) {
        List advisors = Lists.newArrayList();
        Map collocatedClassCache = Maps.newHashMap();
        for (PointcutClass adviceClass : adviceClasses) {
            try {
                advisors.add(new AdviceBuilder(adviceClass).build(collocatedClassCache));
            } catch (Throwable t) {
                logger.error("error creating advice: {}", adviceClass.type().getClassName(), t);
            }
        }
        return advisors;
    }

    private static ImmutableList createReweavableAdvisors(
            List reweavableAdviceConfigs, @Nullable Instrumentation instrumentation,
            File tmpDir, boolean cleanTmpDir) throws Exception {
        ImmutableMap advisors =
                AdviceGenerator.createAdvisors(reweavableAdviceConfigs, null, false, true);
        if (instrumentation == null) {
            // instrumentation is null when debugging with LocalContainer
            ClassLoader isolatedWeavingClassLoader =
                    Thread.currentThread().getContextClassLoader();
            checkNotNull(isolatedWeavingClassLoader);
            ClassLoaders.defineClasses(advisors.values(), isolatedWeavingClassLoader);
        } else {
            if (cleanTmpDir) {
                ClassLoaders.createDirectoryOrCleanPreviousContentsWithPrefix(tmpDir,
                        "custom-advice-");
            }
            ClassLoaders.defineClassesInBootstrapClassLoader(advisors.values(), instrumentation,
                    tmpDir, "custom-advice-");
        }
        return advisors.keySet().asList();
    }

    private static @Nullable PointcutClassName getPointcutClassName(Advice advice) {
        PointcutClassName subTypeRestrictionPointcutClassName = null;
        Pattern subTypeRestrictionPattern = advice.pointcutSubTypeRestrictionPattern();
        if (subTypeRestrictionPattern != null) {
            subTypeRestrictionPointcutClassName =
                    PointcutClassName.fromPattern(subTypeRestrictionPattern, null, false);
        } else {
            String subTypeRestriction = advice.pointcut().subTypeRestriction();
            if (!subTypeRestriction.isEmpty()) {
                subTypeRestrictionPointcutClassName =
                        PointcutClassName.fromNonPattern(subTypeRestriction, null, false);
            }
        }
        Pattern classNamePattern = advice.pointcutClassNamePattern();
        if (classNamePattern != null) {
            return PointcutClassName.fromPattern(classNamePattern,
                    subTypeRestrictionPointcutClassName,
                    advice.pointcut().methodName().equals(""));
        }
        String className = advice.pointcut().className();
        if (!className.isEmpty()) {
            return PointcutClassName.fromNonPattern(className, subTypeRestrictionPointcutClassName,
                    advice.pointcut().methodName().equals(""));
        }
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy