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

com.newrelic.agent.instrumentation.weaver.preprocessors.AgentPostprocessors Maven / Gradle / Ivy

The newest version!
/*
 *
 *  * Copyright 2020 New Relic Corporation. All rights reserved.
 *  * SPDX-License-Identifier: Apache-2.0
 *
 */

package com.newrelic.agent.instrumentation.weaver.preprocessors;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.newrelic.agent.instrumentation.InstrumentationType;
import com.newrelic.agent.instrumentation.context.InstrumentationContext;
import com.newrelic.agent.instrumentation.tracing.TraceClassVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;

import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.agent.bridge.PrivateApi;
import com.newrelic.agent.bridge.PublicApi;
import com.newrelic.agent.bridge.TracedActivity;
import com.newrelic.api.agent.ExternalParameters;
import com.newrelic.api.agent.Insights;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Segment;
import com.newrelic.api.agent.Token;
import com.newrelic.api.agent.TracedMethod;
import com.newrelic.api.agent.Transaction;
import com.newrelic.api.agent.TransactionNamePriority;
import com.newrelic.api.agent.weaver.internal.WeavePackageType;
import com.newrelic.weave.utils.WeaveUtils;
import com.newrelic.weave.weavepackage.WeavePackage;
import com.newrelic.weave.weavepackage.WeavePostprocessor;

public class AgentPostprocessors implements WeavePostprocessor {

    private WeavePackageType weavePackageType = WeavePackageType.CUSTOM;
    private final ConcurrentMap> tracedWeaveInstrumentationDetails = new ConcurrentHashMap<>();

    public void setWeavePackageType(WeavePackageType weavePackageType) {
        this.weavePackageType = weavePackageType;
    }

    /**
     * Set a map where each key is a weave package and each map entry is a set of {@link TracedWeaveInstrumentationTracker}s.
     * 

* Each TracedWeaveInstrumentationTracker represents a method with the TracedWeaveInstrumentation type {@link InstrumentationType}, * which indicates that the method was instrumented via @Trace annotation from weaved instrumentation. * * @param tracedWeaveInstrumentationDetails map of weave packages, each with and associated set of TracedWeaveInstrumentationTrackers */ public void setTracedWeaveInstrumentationDetails(ConcurrentMap> tracedWeaveInstrumentationDetails) { this.tracedWeaveInstrumentationDetails.putAll(tracedWeaveInstrumentationDetails); } @Override public ClassVisitor postprocess(String className, ClassVisitor cv, Set utilityClassesInternalNames, WeavePackage weavePackage, boolean isUtilityClass) { cv = wrapApiCallsForSupportability(cv); // Any class that doesn't have a @Weave or @SkipIfPresent annotation on it is considered to be a utility class. {see com.newrelic.weave.UtilityClass} if (isUtilityClass) { InstrumentationContext context = new InstrumentationContext(null, null, null); this.setTracedWeaveInstrumentationDetails(((AgentPreprocessors) weavePackage.getConfig().getPreprocessor()).getTracedWeaveInstrumentationDetails()); Set tracedWeaveInstrumentationTrackers = tracedWeaveInstrumentationDetails.get(weavePackage.getName()); if (tracedWeaveInstrumentationTrackers != null) { for (TracedWeaveInstrumentationTracker tracedWeaveInstrumentationTracker : tracedWeaveInstrumentationTrackers) { tracedWeaveInstrumentationTracker.addToInstrumentationContext(context, tracedWeaveInstrumentationTracker.getMethod()); } } cv = new TraceClassVisitor(cv, className, context, Collections.emptySet()); } return cv; } ClassVisitor wrapApiCallsForSupportability(ClassVisitor cv) { return new ClassVisitor(WeaveUtils.ASM_API_LEVEL, cv) { @Override public MethodVisitor visitMethod(int access, final String methodName, String methodDesc, String signature, String[] exceptions) { final MethodVisitor mv = super.visitMethod(access, methodName, methodDesc, signature, exceptions); return new GeneratorAdapter(WeaveUtils.ASM_API_LEVEL, mv, access, methodName, methodDesc) { @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { Method currentMethod = new Method(name, desc); Collection methods = TRACKED_API_INTERNAL_NAME_TO_METHOD_NAMES.get(owner); if (methods != null && methods.contains(currentMethod)) { // Set the thread local to the current type of caller (e.g. - FIELD, INTERNAL, CUSTOM, etc) setCurrentApiSource(weavePackageType); // Make the original API call super.visitMethodInsn(opcode, owner, name, desc, itf); // Remove the thread local. If we hit an exception before we get here it will just be // overwritten before the next call. So we don't need to worry about a try/catch/finally unsetCurrentApiSource(); } else { super.visitMethodInsn(opcode, owner, name, desc, itf); } } /** * Generate the instructions required to set the value of the api source thread local. * * e.g: AgentBridge.currentApiSource.set(WeavePackageType.INTERNAL); * * NOTE: If you modify this method you MUST also update "visitMaxs" above to specify the correct * number of parameters that the "currentApiSource" method call pushes on to the stack. * * @param weavePackageType the source of the API caller to set */ private void setCurrentApiSource(WeavePackageType weavePackageType) { getStatic(Type.getType(AgentBridge.class), "currentApiSource", Type.getType(ThreadLocal.class)); getStatic(Type.getType(WeavePackageType.class), weavePackageType.name(), Type.getType(WeavePackageType.class)); invokeVirtual(Type.getType(ThreadLocal.class), new Method("set", "(Ljava/lang/Object;)V")); } /** * Generate the instructions required to unset the value of the api source thread local. * * e.g: AgentBridge.currentApiSource.remove(); */ private void unsetCurrentApiSource() { getStatic(Type.getType(AgentBridge.class), "currentApiSource", Type.getType(ThreadLocal.class)); invokeVirtual(Type.getType(ThreadLocal.class), new Method("remove", "()V")); } @Override public void visitMaxs(int maxStack, int maxLocals) { // We add two variables to the stack when we push the "currentApiSource" static field on the // stack and when we push the "weavePackageType" parameter on the stack (maxStack = 2). super.visitMaxs(maxStack + 2, maxLocals); } }; } }; } void addTrackedApiMethods(Multimap methodsToAdd) { Multimap updatedMultimap = ImmutableMultimap .builder() .putAll(TRACKED_API_INTERNAL_NAME_TO_METHOD_NAMES) .putAll(methodsToAdd) .build(); TRACKED_API_INTERNAL_NAME_TO_METHOD_NAMES = updatedMultimap; } /** * This Multimap contains all of the classes and methods to automatically wrap with the supportability thread local * call. This multimap tracks bridge AND API types and method calls. * * The key of this multimap is the Class that contains the method, and the values are any methods to match. * * Example: * * "com/newrelic/api/agent/Transaction" -> startSegment(String segmentName) * -> startSegment(String category, String segmentName) * * "com/newrelic/agent/bridge/Transaction" -> createAndStartTracedActivity() */ private static Multimap TRACKED_API_INTERNAL_NAME_TO_METHOD_NAMES = ImmutableMultimap .builder() // Transaction API - Token - Token getToken() .put(Type.getType(Transaction.class).getInternalName(), new Method("getToken", Type.getType(Token.class), new Type[] {})) // Transaction Bridge API - Token - Token getToken() .put(Type.getType(com.newrelic.agent.bridge.Transaction.class).getInternalName(), new Method("getToken", Type.getType(com.newrelic.agent.bridge.Token.class), new Type[] {})) // Transaction API - Segment - Segment startSegment(String segmentName) .put(Type.getType(Transaction.class).getInternalName(), new Method("startSegment", Type.getType(Segment.class), new Type[] { Type.getType(String.class) })) // Transaction API - Segment - Segment startSegment(String category, String segmentName) .put(Type.getType(Transaction.class).getInternalName(), new Method("startSegment", Type.getType(Segment.class), new Type[] { Type.getType(String.class), Type.getType(String.class) })) // Transaction Bridge API - Traced Activity - TracedActivity createAndStartTracedActivity() .put(Type.getType(com.newrelic.agent.bridge.Transaction.class).getInternalName(), new Method("createAndStartTracedActivity", Type.getType(TracedActivity.class), new Type[] {})) // Transaction API - Ignore - void ignore() .put(Type.getType(Transaction.class).getInternalName(), new Method("ignore", Type.VOID_TYPE, new Type[] {})) // Transaction API - IgnoreApdex - void ignoreApdex() .put(Type.getType(Transaction.class).getInternalName(), new Method("ignoreApdex", Type.VOID_TYPE, new Type[] {})) // Transaction API - ProcessRequestMetadata - void processRequestMetadata(String requestMetadata) .put(Type.getType(Transaction.class).getInternalName(), new Method("processRequestMetadata", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // Transaction API - ProcessResponseMetadata - void processResponseMetadata(String responseMetadata) .put(Type.getType(Transaction.class).getInternalName(), new Method("processResponseMetadata", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // Transaction API - ProcessResponseMetadata - void processResponseMetadata(String responseMetadata, URI uri) .put(Type.getType(Transaction.class).getInternalName(), new Method("processResponseMetadata", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(URI.class) })) // Transaction API - SetTransactionName - boolean setTransactionName(TransactionNamePriority namePriority, boolean override, String category, String... parts) .put(Type.getType(Transaction.class).getInternalName(), new Method("setTransactionName", Type.BOOLEAN_TYPE, new Type[] { Type.getType(TransactionNamePriority.class), Type.BOOLEAN_TYPE, Type.getType(String.class), Type.getType(String[].class) })) // Transaction Bridge API - SetTransactionName - boolean setTransactionName(TransactionNamePriority namePriority, boolean override, String category, String... parts) .put(Type.getType(com.newrelic.agent.bridge.Transaction.class).getInternalName(), new Method("setTransactionName", Type.BOOLEAN_TYPE, new Type[] { Type.getType(TransactionNamePriority.class), Type.BOOLEAN_TYPE, Type.getType(String.class), Type.getType(String[].class) })) // TracedMethod API - ReportAsExternal - void reportAsExternal(ExternalParameters externalParameters) .put(Type.getType(TracedMethod.class).getInternalName(), new Method("reportAsExternal", Type.VOID_TYPE, new Type[] { Type.getType(ExternalParameters.class) })) // TracedMethod Bridge API - ReportAsExternal - void reportAsExternal(com.newrelic.agent.bridge.external.ExternalParameters externalParameters) .put(Type.getType(com.newrelic.agent.bridge.TracedMethod.class).getInternalName(), new Method("reportAsExternal", Type.VOID_TYPE, new Type[] { Type.getType(com.newrelic.agent.bridge.external.ExternalParameters.class) })) // Insights API - RecordCustomEvent - void recordCustomEvent(String eventType, Map attributes) .put(Type.getType(Insights.class).getInternalName(), new Method("recordCustomEvent", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(Map.class) })) // Segment API - Segment/End - void end() .put(Type.getType(Segment.class).getInternalName(), new Method("end", Type.VOID_TYPE, new Type[] {})) // Segment API - Segment/Ignore - void ignore() .put(Type.getType(Segment.class).getInternalName(), new Method("ignore", Type.VOID_TYPE, new Type[] {})) // Segment API - ReportAsExternal - void reportAsExternal(ExternalParameters externalParameters) .put(Type.getType(Segment.class).getInternalName(), new Method("reportAsExternal", Type.VOID_TYPE, new Type[] { Type.getType(ExternalParameters.class) })) // Segment API - Segment/SetMetricName - void setMetricName(String... metricNameParts) .put(Type.getType(Segment.class).getInternalName(), new Method("setMetricName", Type.VOID_TYPE, new Type[] { Type.getType(String[].class) })) // Traced Method API - Segment/SetMetricName - void setMetricName(String... metricNameParts) .put(Type.getType(TracedMethod.class).getInternalName(), new Method("setMetricName", Type.VOID_TYPE, new Type[] { Type.getType(String[].class) })) // Traced Method Bridge API - Segment/SetMetricName - void setMetricName(String... metricNameParts) .put(Type.getType(com.newrelic.agent.bridge.TracedMethod.class).getInternalName(), new Method("setMetricName", Type.VOID_TYPE, new Type[] { Type.getType(String[].class) })) // Token API - Token/Link - boolean link() .put(Type.getType(Token.class).getInternalName(), new Method("link", Type.BOOLEAN_TYPE, new Type[] {})) // Token API - Token/Expire - boolean expire() .put(Type.getType(Token.class).getInternalName(), new Method("expire", Type.BOOLEAN_TYPE, new Type[] {})) // Token API - Token/Link, Token/Expire - boolean linkAndExpire() .put(Type.getType(Token.class).getInternalName(), new Method("linkAndExpire", Type.BOOLEAN_TYPE, new Type[] {})) // NewRelic API - AddCustomParameter - public static void addCustomParameter(String key, Number value) .put(Type.getType(NewRelic.class).getInternalName(), new Method("addCustomParameter", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(Number.class) })) // NewRelic API - AddCustomParameter - public static void addCustomParameter(String key, String value) .put(Type.getType(NewRelic.class).getInternalName(), new Method("addCustomParameter", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(String.class) })) // Public API - AddCustomParameter - public static void addCustomParameter(String key, Number value) .put(Type.getType(PublicApi.class).getInternalName(), new Method("addCustomParameter", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(Number.class) })) // Public API - AddCustomParameter - public static void addCustomParameter(String key, String value) .put(Type.getType(PublicApi.class).getInternalName(), new Method("addCustomParameter", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(String.class) })) // Private API - AddCustomAttribute - public static void addCustomAttribute(String key, Number value) .put(Type.getType(PrivateApi.class).getInternalName(), new Method("addCustomAttribute", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(Number.class) })) // Private API - AddCustomAttribute - public static void addCustomAttribute(String key, String value) .put(Type.getType(PrivateApi.class).getInternalName(), new Method("addCustomAttribute", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(String.class) })) // Private API - AddCustomAttribute - public static void addCustomAttribute(String key, Map values); .put(Type.getType(PrivateApi.class).getInternalName(), new Method("addCustomAttribute", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(Map.class) })) // NewRelic API - Ignore - public static void ignoreTransaction() .put(Type.getType(NewRelic.class).getInternalName(), new Method("ignoreTransaction", Type.VOID_TYPE, new Type[] {})) // Public API - Ignore - public void ignoreTransaction() .put(Type.getType(PublicApi.class).getInternalName(), new Method("ignoreTransaction", Type.VOID_TYPE, new Type[] {})) // NewRelic API - IgnoreApdex - public static void ignoreApdex() .put(Type.getType(NewRelic.class).getInternalName(), new Method("ignoreApdex", Type.VOID_TYPE, new Type[] {})) // Public API - IgnoreApdex - public void ignoreApdex() .put(Type.getType(PublicApi.class).getInternalName(), new Method("ignoreApdex", Type.VOID_TYPE, new Type[] {})) // NewRelic API - NoticeError - public static void noticeError(Throwable throwable, Map params, boolean expected) .put(Type.getType(NewRelic.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(Throwable.class), Type.getType(Map.class), Type.BOOLEAN_TYPE })) // NewRelic API - NoticeError - public static void noticeError(Throwable throwable, boolean expected) .put(Type.getType(NewRelic.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(Throwable.class), Type.BOOLEAN_TYPE })) // NewRelic API - NoticeError - public static void noticeError(String message, Map params, boolean expected) .put(Type.getType(NewRelic.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(Map.class), Type.BOOLEAN_TYPE })) // NewRelic API - NoticeError - public static void noticeError(String message, boolean expected) .put(Type.getType(NewRelic.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.BOOLEAN_TYPE })) // NewRelic API - NoticeError - public static void noticeError(Throwable throwable, Map params) .put(Type.getType(NewRelic.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(Throwable.class), Type.getType(Map.class) })) // NewRelic API - NoticeError - public static void noticeError(Throwable throwable) .put(Type.getType(NewRelic.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(Throwable.class) })) // NewRelic API - NoticeError - public static void noticeError(String message, Map params) .put(Type.getType(NewRelic.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(Map.class) })) // NewRelic API - NoticeError - public static void noticeError(String message) .put(Type.getType(NewRelic.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // Public API - NoticeError - public static void noticeError(Throwable throwable, Map params, boolean expected) .put(Type.getType(PublicApi.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(Throwable.class), Type.getType(Map.class), Type.BOOLEAN_TYPE })) // Public API - NoticeError - public static void noticeError(Throwable throwable, boolean expected) .put(Type.getType(PublicApi.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(Throwable.class), Type.BOOLEAN_TYPE })) // Public API - NoticeError - public static void noticeError(String message, Map params, boolean expected) .put(Type.getType(PublicApi.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(Map.class), Type.BOOLEAN_TYPE })) // Public API - NoticeError - public static void noticeError(String message, boolean expected) .put(Type.getType(PublicApi.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.BOOLEAN_TYPE })) // Public API - NoticeError - public static void noticeError(Throwable throwable, Map params) .put(Type.getType(PublicApi.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(Throwable.class), Type.getType(Map.class) })) // Public API - NoticeError - public static void noticeError(Throwable throwable) .put(Type.getType(PublicApi.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(Throwable.class) })) // Public API - NoticeError - public static void noticeError(String message, Map params) .put(Type.getType(PublicApi.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(Map.class) })) // Public API - NoticeError - public static void noticeError(String message) .put(Type.getType(PublicApi.class).getInternalName(), new Method("noticeError", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // NewRelic API - SetAppServerPort - public static void setAppServerPort(int port) .put(Type.getType(NewRelic.class).getInternalName(), new Method("setAppServerPort", Type.VOID_TYPE, new Type[] { Type.INT_TYPE })) // Public API - SetAppServerPort - public static void setAppServerPort(int port) .put(Type.getType(PublicApi.class).getInternalName(), new Method("setAppServerPort", Type.VOID_TYPE, new Type[] { Type.INT_TYPE })) // Private API - SetAppServerPort - public static void setAppServerPort(int port) .put(Type.getType(PrivateApi.class).getInternalName(), new Method("setAppServerPort", Type.VOID_TYPE, new Type[] { Type.INT_TYPE })) // NewRelic API - SetInstanceName - public static void setInstanceName(String instanceName) .put(Type.getType(NewRelic.class).getInternalName(), new Method("setInstanceName", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // Public API - SetInstanceName - public static void setInstanceName(String instanceName) .put(Type.getType(PublicApi.class).getInternalName(), new Method("setInstanceName", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // Private API - SetInstanceName - public static void setInstanceName(String instanceName) .put(Type.getType(PrivateApi.class).getInternalName(), new Method("setInstanceName", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // NewRelic API - SetProductName - public static void setProductName(String name) .put(Type.getType(NewRelic.class).getInternalName(), new Method("setProductName", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // Public API - SetProductName - public static void setProductName(String name) .put(Type.getType(PublicApi.class).getInternalName(), new Method("setProductName", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // NewRelic API - SetServerInfo - public static void setServerInfo(String dispatcherName, String version) .put(Type.getType(NewRelic.class).getInternalName(), new Method("setServerInfo", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(String.class) })) // Public API - SetServerInfo - public static void setServerInfo(String dispatcherName, String version) .put(Type.getType(PublicApi.class).getInternalName(), new Method("setServerInfo", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(String.class) })) // Private API - SetServerInfo - public static void setServerInfo(String serverInfo) .put(Type.getType(PrivateApi.class).getInternalName(), new Method("setServerInfo", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // NewRelic API - SetTransactionName - public static void setTransactionName(String category, String name) .put(Type.getType(NewRelic.class).getInternalName(), new Method("setTransactionName", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(String.class) })) // Public API - SetTransactionName - public static void setTransactionName(String category, String name) .put(Type.getType(PublicApi.class).getInternalName(), new Method("setTransactionName", Type.VOID_TYPE, new Type[] { Type.getType(String.class), Type.getType(String.class) })) // NewRelic API - SetUserName - public static void setUserName(String name) .put(Type.getType(NewRelic.class).getInternalName(), new Method("setUserName", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // Public API - SetUserName - public static void setUserName(String name) .put(Type.getType(PublicApi.class).getInternalName(), new Method("setUserName", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // NewRelic API - SetAccountName - public static void setAccountName(String name) .put(Type.getType(NewRelic.class).getInternalName(), new Method("setAccountName", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) // Public API - SetAccountName - public static void setAccountName(String name) .put(Type.getType(PublicApi.class).getInternalName(), new Method("setAccountName", Type.VOID_TYPE, new Type[] { Type.getType(String.class) })) .build(); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy