/** * Copyright 2014 SAP AG * * 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.aim.mainagent; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.aim.api.exceptions.InstrumentationException; import org.aim.api.instrumentation.description.internal.FlatInstrumentationEntity; import org.aim.api.instrumentation.description.internal.InstrumentationSet; import org.aim.description.InstrumentationDescription; import org.aim.description.restrictions.Restriction; import org.aim.description.scopes.MethodsEnclosingScope; import org.aim.logging.AIMLogger; import org.aim.logging.AIMLoggerFactory; import org.aim.mainagent.instrumentor.BCInjector; import org.aim.mainagent.instrumentor.JAgentSwapper; import org.aim.mainagent.instrumentor.JInstrumentation; import org.aim.mainagent.scope.ScopeAnalysisController; /** * Keeps track of and conducts method instrumentation. * * @author Alexander Wert * */ public class MethodInstrumentor implements IInstrumentor { private static final AIMLogger LOGGER = AIMLoggerFactory.getLogger(MethodInstrumentor.class); private final Set<FlatInstrumentationEntity> currentInstrumentationState = new HashSet<>(); @SuppressWarnings("rawtypes") @Override public void instrument(InstrumentationDescription descr) throws InstrumentationException { if (!containsValidInstrumentationInstructions(descr)) { return; } ScopeAnalysisController scopeAnalyzer = new ScopeAnalysisController(descr); // TODO: // usage // with // IDM Class[] classes = JInstrumentation.getInstance().getjInstrumentation().getAllLoadedClasses(); // copy all classes to a new list as the array is not allowed to be // modified! List<Class> allLoadedClasses = new ArrayList<>(); allLoadedClasses.addAll(Arrays.asList(classes)); Set<FlatInstrumentationEntity> newInstrumentationStatements = scopeAnalyzer.resolveScopes(allLoadedClasses); Set<Class<?>> overLappingClasses = revertOverlappingInstrumentation(newInstrumentationStatements); for (FlatInstrumentationEntity oldEntity : getCurrentInstrumentationState()) { if (overLappingClasses.contains(oldEntity.getClazz())) { newInstrumentationStatements.add(oldEntity); } } InstrumentationSet newInstrumentationSet = new InstrumentationSet(newInstrumentationStatements); LOGGER.info("Going to instrument the following methods:"); for (FlatInstrumentationEntity fie : newInstrumentationStatements) { LOGGER.info("{}", fie.getMethodSignature()); } injectNewInstrumentation(newInstrumentationSet, descr.getGlobalRestriction()); getCurrentInstrumentationState().addAll(newInstrumentationStatements); } private boolean containsValidInstrumentationInstructions(InstrumentationDescription descr) { return descr.containsScopeType(MethodsEnclosingScope.class); } private void injectNewInstrumentation(InstrumentationSet newInstrumentationSet, Restriction instrumentationRestriction) throws InstrumentationException { Map<Class<?>, byte[]> classesToRevert = BCInjector.getInstance().injectInstrumentationProbes( newInstrumentationSet, instrumentationRestriction); JAgentSwapper.getInstance().redefineClasses(classesToRevert); } private Set<Class<?>> revertOverlappingInstrumentation(Set<FlatInstrumentationEntity> newInstrumentationStatements) throws InstrumentationException { Set<Class<?>> intersection = new InstrumentationSet(newInstrumentationStatements).classesToInstrument(); intersection.retainAll(new InstrumentationSet(getCurrentInstrumentationState()).classesToInstrument()); Map<Class<?>, byte[]> classesToRevert = BCInjector.getInstance().partlyRevertInstrumentation(intersection); JAgentSwapper.getInstance().redefineClasses(classesToRevert); return intersection; } @Override public void undoInstrumentation() throws InstrumentationException { Map<Class<?>, byte[]> classesToRevert = BCInjector.getInstance().revertInstrumentation(); JAgentSwapper.getInstance().redefineClasses(classesToRevert); getCurrentInstrumentationState().clear(); } /** * @return the currentInstrumentationState */ public Set<FlatInstrumentationEntity> getCurrentInstrumentationState() { return currentInstrumentationState; } }