package com.navercorp.pinpoint.plugin.db2; import static com.navercorp.pinpoint.common.util.VarArgs.va; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentException; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod; import com.navercorp.pinpoint.bootstrap.instrument.Instrumentor; import com.navercorp.pinpoint.bootstrap.instrument.MethodFilter; import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformCallback; import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplate; import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplateAware; import com.navercorp.pinpoint.bootstrap.interceptor.Interceptor; import com.navercorp.pinpoint.bootstrap.interceptor.scope.ExecutionPolicy; import com.navercorp.pinpoint.bootstrap.logging.PLogger; import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin; import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginSetupContext; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.BindValueAccessor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.DatabaseInfoAccessor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.PreparedStatementBindingMethodFilter; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.PreparedStatementBindingMethodFilter; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.PreparedStatementBindVariableInterceptor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.PreparedStatementExecuteQueryInterceptor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.StatementExecuteQueryInterceptor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.StatementExecuteUpdateInterceptor; import com.navercorp.pinpoint.bootstrap.plugin.util.InstrumentUtils; import java.security.ProtectionDomain; import java.util.List; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.StatementCreateInterceptor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.ConnectionCloseInterceptor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.PreparedStatementCreateInterceptor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.TransactionSetAutoCommitInterceptor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.TransactionCommitInterceptor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.TransactionRollbackInterceptor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.DriverConnectInterceptorV2; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.ParsingResultAccessor; import com.navercorp.pinpoint.bootstrap.plugin.jdbc.interceptor.CallableStatementRegisterOutParameterInterceptor; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod; public class DB2Plugin implements ProfilerPlugin, TransformTemplateAware { private static final String DB2_SCOPE = DB2PluginConstants.DB2_SCOPE; private final PLogger logger = PLoggerFactory.getLogger(this.getClass()); private final DB2JdbcUrlParser jdbcUrlParser = new DB2JdbcUrlParser(); private TransformTemplate transformTemplate; @Override public void setup(ProfilerPluginSetupContext context) { DB2Config config = new DB2Config(context.getConfig()); if (!config.isPluginEnable()) { logger.info(">>>>>>>>>>>>>>>>>>>>>>>>{} disabled", this.getClass().getSimpleName()); return; } logger.info(">>>>>>>>>>>>>>>>>>>>>>{} config:{}", this.getClass().getSimpleName(), config); context.addJdbcUrlParser(jdbcUrlParser); addConnectionTransformer(); addDriverTransformer(); //addPreparedStatementTransformer(); //addCallableStatementTransformer(); //addStatementTransformer(); } private void addConnectionTransformer() { logger.info(">>>>>>>>>>>>>>>>>>>>>> IN SIDE addConnectionTransformer"); transformTemplate.transform("com.ibm.db2.jcc.DB2Connection", DB2ConnectionTransform.class); } public static class DB2ConnectionTransform implements TransformCallback { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { DB2Config config = new DB2Config(instrumentor.getProfilerConfig()); InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); if (!target.isInterceptable()) { return null; } target.addField(DatabaseInfoAccessor.class); // close InstrumentUtils.findMethod(target, "close") .addScopedInterceptor(ConnectionCloseInterceptor.class, DB2_SCOPE); // createStatement final Class statementCreate = StatementCreateInterceptor.class; InstrumentUtils.findMethod(target, "createStatement") .addScopedInterceptor(statementCreate, DB2_SCOPE); InstrumentUtils.findMethod(target, "createStatement", "int", "int") .addScopedInterceptor(statementCreate, DB2_SCOPE); InstrumentUtils.findMethod(target, "createStatement", "int", "int", "int") .addScopedInterceptor(statementCreate, DB2_SCOPE); // preparedStatement final Class preparedStatementCreate = PreparedStatementCreateInterceptor.class; InstrumentUtils.findMethod(target, "prepareStatement", "java.lang.String") .addScopedInterceptor(preparedStatementCreate, DB2_SCOPE); InstrumentUtils.findMethod(target, "prepareStatement", "java.lang.String", "int") .addScopedInterceptor(preparedStatementCreate, DB2_SCOPE); InstrumentUtils.findMethod(target, "prepareStatement", "java.lang.String", "int[]") .addScopedInterceptor(preparedStatementCreate, DB2_SCOPE); InstrumentUtils .findMethod(target, "prepareStatement", "java.lang.String", "java.lang.String[]") .addScopedInterceptor(preparedStatementCreate, DB2_SCOPE); InstrumentUtils.findMethod(target, "prepareStatement", "java.lang.String", "int", "int") .addScopedInterceptor(preparedStatementCreate,DB2_SCOPE); InstrumentUtils .findMethod(target, "prepareStatement", "java.lang.String", "int", "int", "int") .addScopedInterceptor(preparedStatementCreate, DB2_SCOPE); // preparecall InstrumentUtils.findMethod(target, "prepareCall", "java.lang.String") .addScopedInterceptor(preparedStatementCreate, DB2_SCOPE); InstrumentUtils.findMethod(target, "prepareCall", "java.lang.String", "int", "int") .addScopedInterceptor(preparedStatementCreate, DB2_SCOPE); InstrumentUtils .findMethod(target, "prepareCall", "java.lang.String", "int", "int", "int") .addScopedInterceptor(preparedStatementCreate, DB2_SCOPE); if (config.isProfileSetAutoCommit()) { InstrumentUtils.findMethod(target, "setAutoCommit", "boolean") .addScopedInterceptor(TransactionSetAutoCommitInterceptor.class, DB2_SCOPE); } if (config.isProfileCommit()) { InstrumentUtils.findMethod(target, "commit") .addScopedInterceptor(TransactionCommitInterceptor.class, DB2_SCOPE); } if (config.isProfileRollback()) { InstrumentUtils.findMethod(target, "rollback") .addScopedInterceptor(TransactionRollbackInterceptor.class, DB2_SCOPE); } return target.toBytecode(); } }; private void addDriverTransformer() { logger.info(">>>>>>>>>>>>>>>>>>>>>> IN SIDE addDriverTransformer"); transformTemplate.transform("com.ibm.db2.jcc.DB2Driver", DriverTransformer.class); } public static class DriverTransformer implements TransformCallback { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); target.addField(DatabaseInfoAccessor.class); InstrumentUtils.findMethod( target, "connect", "java.lang.String", "java.util.Properties").addScopedInterceptor(DriverConnectInterceptorV2.class, va(DB2PluginConstants.DB2_SCOPE, true), DB2_SCOPE, ExecutionPolicy.ALWAYS); return target.toBytecode(); } }; private void addPreparedStatementTransformer() { logger.info(">>>>>>>>>>>>>>>>>>>>>> IN SIDE addPreparedStatementTransformer"); transformTemplate.transform("com.ibm.db2.jcc.DB2PreparedStatement", PreparedStatementTransform.class); } public static class PreparedStatementTransform implements TransformCallback { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { DB2Config config = new DB2Config(instrumentor.getProfilerConfig()); InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); target.addField(DatabaseInfoAccessor.class); target.addField(ParsingResultAccessor.class); target.addField(BindValueAccessor.class); int maxBindValueSize = config.getMaxSqlBindValueSize(); final Class preparedStatementInterceptor = PreparedStatementExecuteQueryInterceptor.class; InstrumentUtils.findMethod(target, "execute") .addScopedInterceptor(preparedStatementInterceptor, va(maxBindValueSize), DB2_SCOPE); InstrumentUtils.findMethod(target, "executeQuery") .addScopedInterceptor(preparedStatementInterceptor, va(maxBindValueSize), DB2_SCOPE); InstrumentUtils.findMethod(target, "executeUpdate") .addScopedInterceptor(preparedStatementInterceptor, va(maxBindValueSize), DB2_SCOPE); if (config.isTraceSqlBindValue()) { final PreparedStatementBindingMethodFilter excludes = PreparedStatementBindingMethodFilter .excludes("setRowId", "setNClob", "setSQLXML"); final List declaredMethods = target.getDeclaredMethods(excludes); for (InstrumentMethod method : declaredMethods) { method.addScopedInterceptor(PreparedStatementBindVariableInterceptor.class, DB2_SCOPE, ExecutionPolicy.BOUNDARY); } } return target.toBytecode(); } } ; private void addStatementTransformer() { logger.info(">>>>>>>>>>>>>>>>>>>>>> IN SIDE addStatementTransformer"); transformTemplate.transform("com.ibm.db2.jcc.DB2ServerStatement", DB2StatementTransform.class); } private void addCallableStatementTransformer() { logger.info(">>>>>>>>>>>>>>>>>>>>>> IN SIDE addCallableStatementTransformer"); transformTemplate.transform("com.ibm.db2.jcc.DB2CallableStatement", DB2CallableStatementTransform.class); } public static class DB2StatementTransform implements TransformCallback { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); if (!target.isInterceptable()) { return null; } target.addField(DatabaseInfoAccessor.class); final Class executeQueryInterceptor = StatementExecuteQueryInterceptor.class; InstrumentUtils.findMethod(target, "executeQuery", "java.lang.String") .addScopedInterceptor(executeQueryInterceptor, DB2_SCOPE); final Class executeUpdateInterceptor = StatementExecuteUpdateInterceptor.class; InstrumentUtils.findMethod(target, "executeUpdate", "java.lang.String") .addScopedInterceptor(executeUpdateInterceptor, DB2_SCOPE); InstrumentUtils.findMethod(target, "executeUpdate", "java.lang.String", "int") .addScopedInterceptor(executeUpdateInterceptor, DB2_SCOPE); InstrumentUtils.findMethod(target, "execute", "java.lang.String") .addScopedInterceptor(executeUpdateInterceptor, DB2_SCOPE); InstrumentUtils.findMethod(target, "execute", "java.lang.String", "int") .addScopedInterceptor(executeUpdateInterceptor, DB2_SCOPE); return target.toBytecode(); } }; public static class DB2CallableStatementTransform implements TransformCallback { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); if (!target.isInterceptable()) { return null; } target.addField(DatabaseInfoAccessor.class); target.addField(ParsingResultAccessor.class); target.addField(BindValueAccessor.class); final Class callableStatementInterceptor = CallableStatementRegisterOutParameterInterceptor.class; InstrumentUtils.findMethod(target, "registerOutParameter", "int", "int") .addScopedInterceptor(callableStatementInterceptor, DB2_SCOPE); InstrumentUtils.findMethod(target, "registerOutParameter", "int", "int", "int") .addScopedInterceptor(callableStatementInterceptor, DB2_SCOPE); InstrumentUtils.findMethod(target, "registerOutParameter", "int", "int", "java.lang.String") .addScopedInterceptor(callableStatementInterceptor, DB2_SCOPE); return target.toBytecode(); } }; @Override public void setTransformTemplate(TransformTemplate transformTemplate) { this.transformTemplate = transformTemplate; } }