diff --git a/src/main/java/com/profiler/Agent.java b/src/main/java/com/profiler/Agent.java index d65ce2819..572c1eb79 100644 --- a/src/main/java/com/profiler/Agent.java +++ b/src/main/java/com/profiler/Agent.java @@ -44,42 +44,8 @@ public class Agent { // agent의 상태, private volatile AgentStatus agentStatus; - private Thread heartbeatThread; + private HeartBitChecker heartBitChecker; - /** - * collector로 heartbeat을 보낸다. heartbeat의 내용은 agent info. - */ - private Runnable heartBitCommand = new Runnable() { - @Override - public void run() { - if (logger.isLoggable(Level.INFO)) { - logger.info("Send startup information to HIPPO server via " + priorityDataSender.getClass().getSimpleName() + ". agentInfo=" + agentInfo); - } - priorityDataSender.send(agentInfo); - priorityDataSender.send(agentInfo); - priorityDataSender.send(agentInfo); - - long heartbeatInterval = profilerConfig.getHeartbeatInterval(); - if (logger.isLoggable(Level.INFO)) { - logger.info("Starting agent heartbeat. heartbeatInterval:" + heartbeatInterval); - } - while (true) { - if (agentStatus == AgentStatus.RUNNING) { - logger.fine("Send heartbeat"); - priorityDataSender.send(agentInfo); - } else if (agentStatus == AgentStatus.STOPPING || agentStatus == AgentStatus.STOPPED) { - break; - } - // TODO 정밀한 시간계산 없이 일단 그냥 interval 단위로 보냄. - try { - Thread.sleep(heartbeatInterval); - } catch (InterruptedException e) { - break; - } - } - logger.info(Thread.currentThread().getName() + " stopped."); - } - }; public Agent(ProfilerConfig profilerConfig) { changeStatus(AgentStatus.INITIALIZING); @@ -111,18 +77,12 @@ public class Agent { ApiMappingTable.findApiId("test", null, null); this.agentInfo = createAgentInfo(); - this.heartbeatThread = createHeartbeatThread(); + this.heartBitChecker = new HeartBitChecker(priorityDataSender, profilerConfig.getHeartbeatInterval(), agentInfo); + SingletonHolder.INSTANCE = this; } - private Thread createHeartbeatThread() { - Thread thread = new Thread(heartBitCommand); - thread.setName("HIPPO-Agent-Heartbeat-Thread"); - thread.setDaemon(true); - return thread; - } - private AgentInfo createAgentInfo() { String ip = getServerInfo().getHostip(); String ports = ""; @@ -247,14 +207,15 @@ public class Agent { */ public void started() { changeStatus(AgentStatus.RUNNING); - this.heartbeatThread.start(); + this.heartBitChecker.start(); } public void stop() { logger.info("Stopping HIPPO Agent."); changeStatus(AgentStatus.STOPPING); - heartbeatThread.interrupt(); + this.heartBitChecker.close(); + systemMonitor.stop(); diff --git a/src/main/java/com/profiler/AgentURLClassLoader.java b/src/main/java/com/profiler/AgentClassLoader.java similarity index 86% rename from src/main/java/com/profiler/AgentURLClassLoader.java rename to src/main/java/com/profiler/AgentClassLoader.java index b8a36d745..c7243f0ce 100644 --- a/src/main/java/com/profiler/AgentURLClassLoader.java +++ b/src/main/java/com/profiler/AgentClassLoader.java @@ -8,15 +8,14 @@ import java.net.URLClassLoader; /** * */ -public class AgentURLClassLoader { +public class AgentClassLoader { - private ClassPathResolver classPathResolver = new ClassPathResolver(null); private URLClassLoader classLoader; private String bootClass = "com.profiler.AgentBootStrap"; private String bootMethod = "boot"; - public AgentURLClassLoader(URL[] urls) { - ClassLoader classLoader = AgentURLClassLoader.class.getClassLoader(); + public AgentClassLoader(URL[] urls) { + ClassLoader classLoader = AgentClassLoader.class.getClassLoader(); this.classLoader = new URLClassLoader(urls, classLoader); } diff --git a/src/main/java/com/profiler/HeartBitChecker.java b/src/main/java/com/profiler/HeartBitChecker.java new file mode 100644 index 000000000..814eda571 --- /dev/null +++ b/src/main/java/com/profiler/HeartBitChecker.java @@ -0,0 +1,75 @@ +package com.profiler; + +import com.profiler.common.dto.thrift.AgentInfo; +import com.profiler.sender.DataSender; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + */ +public class HeartBitChecker { + private final Logger logger = Logger.getLogger(this.getClass().getName()); + + private long heartBitInterVal; + private DataSender dataSender; + private AgentInfo agentInfo; + + private Thread ioThread; + + + public HeartBitChecker(DataSender dataSender, long heartBitInterVal, AgentInfo agentInfo) { + this.dataSender = dataSender; + this.heartBitInterVal = heartBitInterVal; + this.agentInfo = agentInfo; + } + + + public void start() { + if (logger.isLoggable(Level.INFO)) { + logger.info("Send startup information to HIPPO server via " + dataSender.getClass().getSimpleName() + ". agentInfo=" + agentInfo); + } + dataSender.send(agentInfo); + dataSender.send(agentInfo); + dataSender.send(agentInfo); + + this.ioThread = new Thread(heartBitCommand, "HIPPO-Agent-Heartbeat-Thread"); + this.ioThread.setDaemon(true); + ioThread.start(); + } + + + private Runnable heartBitCommand = new Runnable() { + @Override + public void run() { + + if (logger.isLoggable(Level.INFO)) { + logger.info("Starting agent heartbeat. heartbeatInterval:" + heartBitInterVal); + } + while (true) { + dataSender.send(agentInfo); + + // TODO 정밀한 시간계산 없이 일단 그냥 interval 단위로 보냄. + try { + Thread.sleep(heartBitInterVal); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + logger.info("HeartBitChecker ioThread stopped."); + } + }; + + + public void close() { + logger.info("HeartBitChecker stop"); + ioThread.interrupt(); + try { + ioThread.join(1000*5); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } +} diff --git a/src/main/java/com/profiler/TomcatProfiler.java b/src/main/java/com/profiler/TomcatProfiler.java index f2ea5685f..2914f244c 100644 --- a/src/main/java/com/profiler/TomcatProfiler.java +++ b/src/main/java/com/profiler/TomcatProfiler.java @@ -1,12 +1,5 @@ package com.profiler; -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.IllegalClassFormatException; -import java.lang.instrument.Instrumentation; -import java.security.ProtectionDomain; -import java.util.logging.Level; -import java.util.logging.Logger; - import com.profiler.config.ProfilerConfig; import com.profiler.interceptor.bci.ByteCodeInstrumentor; import com.profiler.interceptor.bci.JavaAssistByteCodeInstrumentor; @@ -14,121 +7,148 @@ import com.profiler.modifier.DefaultModifierRegistry; import com.profiler.modifier.Modifier; import com.profiler.modifier.ModifierRegistry; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; +import java.util.Properties; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + public class TomcatProfiler implements ClassFileTransformer { - private static final Logger logger = Logger.getLogger(TomcatProfiler.class.getName()); + private static final Logger logger = Logger.getLogger(TomcatProfiler.class.getName()); + private boolean isFine = logger.isLoggable(Level.FINE); - private String agentArgString = ""; + private String agentArgString = ""; - private Instrumentation instrumentation; - private final ByteCodeInstrumentor byteCodeInstrumentor; + private Instrumentation instrumentation; + private final ByteCodeInstrumentor byteCodeInstrumentor; - private final ModifierRegistry modifierRepository; + private final ModifierRegistry modifierRepository; - private final ProfilerConfig profilerConfig; - private final Agent agent; + private final ProfilerConfig profilerConfig; + private final Agent agent; - public static void premain(String agentArgs, Instrumentation instrumentation) { - try { - ProfilerConfig profilerConfig = new ProfilerConfig(); - profilerConfig.readConfigFile(); - if (!profilerConfig.isProfileEnable()) { - logger.warning("Profiler Agent not started. profile.enable=" + profilerConfig.isProfileEnable()); - return; - } - Agent agent = new Agent(profilerConfig); - new TomcatProfiler(agentArgs, instrumentation, agent, profilerConfig); - } catch (Exception e) { - logger.log(Level.SEVERE, "Profiler Agent start fail. Cause:" + e.getMessage(), e); - } - } + private AgentClassLoader agentClassLoader; - public TomcatProfiler(String agentArgs, Instrumentation instrumentation, Agent agent, ProfilerConfig profilerConfig) { - this.agentArgString = agentArgs; - this.profilerConfig = profilerConfig; - this.agent = agent; + public static void premain(String agentArgs, Instrumentation instrumentation) { + if (agentArgs != null) { + logger.info("HIPPO agentArgs:" + agentArgs); + } + dumpSystemProperties(); - this.instrumentation = instrumentation; - this.instrumentation.addTransformer(this); + try { + ProfilerConfig profilerConfig = new ProfilerConfig(); + profilerConfig.readConfigFile(); + if (!profilerConfig.isProfileEnable()) { + logger.warning("Profiler Agent not started. profile.enable=" + profilerConfig.isProfileEnable()); + return; + } + Agent agent = new Agent(profilerConfig); + new TomcatProfiler(agentArgs, instrumentation, agent, profilerConfig); + } catch (Exception e) { + logger.log(Level.SEVERE, "Profiler Agent start fail. Cause:" + e.getMessage(), e); + } + } - String[] paths = getTomcatlibPath(); - this.byteCodeInstrumentor = new JavaAssistByteCodeInstrumentor(paths); + private static void dumpSystemProperties() { + if (logger.isLoggable(Level.FINE)) { + Properties properties = System.getProperties(); + Set strings = properties.stringPropertyNames(); + for (String key : strings) { + logger.fine("SystemProperties " + key + "=" + properties.get(key)); + } + } + } - this.modifierRepository = createModifierRegistry(); - } + public TomcatProfiler(String agentArgs, Instrumentation instrumentation, Agent agent, ProfilerConfig profilerConfig) { + this.agentArgString = agentArgs; + this.profilerConfig = profilerConfig; + this.agent = agent; - private String[] getTomcatlibPath() { - String catalinaHome = System.getProperty("catalina.home"); + this.instrumentation = instrumentation; + this.instrumentation.addTransformer(this); - if (catalinaHome == null) { - logger.info("CATALINA_HOME is null"); - return null; - } + String[] paths = getTomcatlibPath(); + this.byteCodeInstrumentor = new JavaAssistByteCodeInstrumentor(paths); - if (logger.isLoggable(Level.INFO)) { - logger.info("CATALINA_HOME=" + catalinaHome); - } + this.modifierRepository = createModifierRegistry(); + } - // TODO This is draft. How can we support both Tomcat and BLOC without this configuration? - String type = System.getProperty("hippo.servertype", "tomcat"); + private String[] getTomcatlibPath() { + String catalinaHome = System.getProperty("catalina.home"); - if (type.equals("bloc")) { - return new String[] { catalinaHome + "/server/lib/catalina.jar", catalinaHome + "/common/lib/servlet-api.jar" }; - } else { - return new String[] { catalinaHome + "/lib/servlet-api.jar", catalinaHome + "/lib/catalina.jar" }; - } - } + if (catalinaHome == null) { + logger.info("CATALINA_HOME is null"); + return null; + } - private ModifierRegistry createModifierRegistry() { - DefaultModifierRegistry modifierRepository = new DefaultModifierRegistry(byteCodeInstrumentor, agent, profilerConfig); - - modifierRepository.addMethodModifier(); - - modifierRepository.addTomcatModifier(); + if (logger.isLoggable(Level.INFO)) { + logger.info("CATALINA_HOME=" + catalinaHome); + } - // jdbc - modifierRepository.addJdbcModifier(); + // TODO This is draft. How can we support both Tomcat and BLOC without this configuration? + String type = System.getProperty("hippo.servertype", "tomcat"); - // rpc - modifierRepository.addConnectorModifier(); + if (type.equals("bloc")) { + return new String[]{catalinaHome + "/server/lib/catalina.jar", catalinaHome + "/common/lib/servlet-api.jar"}; + } else { + return new String[]{catalinaHome + "/lib/servlet-api.jar", catalinaHome + "/lib/catalina.jar"}; + } + } - // bloc - modifierRepository.addBLOCModifier(); + private ModifierRegistry createModifierRegistry() { + DefaultModifierRegistry modifierRepository = new DefaultModifierRegistry(byteCodeInstrumentor, agent, profilerConfig); - return modifierRepository; - } + modifierRepository.addMethodModifier(); - @Override - public byte[] transform(ClassLoader classLoader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classFileBuffer) throws IllegalClassFormatException { - // fast java class skip - if (className.startsWith("java")) { - if (className.startsWith("/", 4) || className.startsWith("x/", 4)) { - return classFileBuffer; - } - } - Modifier findModifier = this.modifierRepository.findModifier(className); - - if (findModifier == null) { - // TODO : 디버그 용도로 추가함 - // TODO : modifier가 중복 적용되면 어떻게 되지??? - if (profilerConfig.isProfilableClass(className)) { - // 테스트 장비에서 callstack view가 잘 보이는지 확인하려고 추가함. - findModifier = this.modifierRepository.findModifier("*"); - } else { - return null; - } - } - - if (logger.isLoggable(Level.FINE)) { - logger.fine("[transform] cl" + classLoader + " className:" + className + " Modifier:" + findModifier.getClass().getName()); - } - String javassistClassName = className.replace('/', '.'); + modifierRepository.addTomcatModifier(); - try { - return findModifier.modify(classLoader, javassistClassName, protectionDomain, classFileBuffer); - } catch (Exception e) { - logger.log(Level.SEVERE, "Modifier:" + findModifier.getTargetClass() + " modify fail. Cause:" + e.getMessage(), e); - return null; - } - } + // jdbc + modifierRepository.addJdbcModifier(); + + // rpc + modifierRepository.addConnectorModifier(); + + // bloc + modifierRepository.addBLOCModifier(); + + return modifierRepository; + } + + @Override + public byte[] transform(ClassLoader classLoader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classFileBuffer) throws IllegalClassFormatException { + // fast java class skip + if (className.startsWith("java")) { + if (className.startsWith("/", 4) || className.startsWith("x/", 4)) { + return classFileBuffer; + } + } + Modifier findModifier = this.modifierRepository.findModifier(className); + + if (findModifier == null) { + // TODO : 디버그 용도로 추가함 + // TODO : modifier가 중복 적용되면 어떻게 되지??? + if (profilerConfig.isProfilableClass(className)) { + // 테스트 장비에서 callstack view가 잘 보이는지 확인하려고 추가함. + findModifier = this.modifierRepository.findModifier("*"); + } else { + return null; + } + } + + if (isFine) { + logger.fine("[transform] cl" + classLoader + " className:" + className + " Modifier:" + findModifier.getClass().getName()); + } + String javassistClassName = className.replace('/', '.'); + + try { + return findModifier.modify(classLoader, javassistClassName, protectionDomain, classFileBuffer); + } catch (Exception e) { + logger.log(Level.SEVERE, "Modifier:" + findModifier.getTargetClass() + " modify fail. Cause:" + e.getMessage(), e); + return null; + } + } } diff --git a/src/main/java/com/profiler/config/ProfilerConfig.java b/src/main/java/com/profiler/config/ProfilerConfig.java index 3614d8b69..eb8f93b0f 100644 --- a/src/main/java/com/profiler/config/ProfilerConfig.java +++ b/src/main/java/com/profiler/config/ProfilerConfig.java @@ -37,7 +37,7 @@ public class ProfilerConfig { private Set profileInclude = new HashSet(4); private Set profileIncludeSub = new HashSet(4); - private long heartbeatInterval = 5000L; + private long heartbeatInterval = 5*60*1000L; public ProfilerConfig() { } diff --git a/src/test/java/com/profiler/AgentURLClassLoaderTest.java b/src/test/java/com/profiler/AgentClassLoaderTest.java similarity index 78% rename from src/test/java/com/profiler/AgentURLClassLoaderTest.java rename to src/test/java/com/profiler/AgentClassLoaderTest.java index f22e55701..1db3dff22 100644 --- a/src/test/java/com/profiler/AgentURLClassLoaderTest.java +++ b/src/test/java/com/profiler/AgentClassLoaderTest.java @@ -15,7 +15,7 @@ import java.security.ProtectionDomain; /** * */ -public class AgentURLClassLoaderTest { +public class AgentClassLoaderTest { private Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -32,14 +32,14 @@ public class AgentURLClassLoaderTest { logger.info("load testlib:" + testJarPath); File file = new File(testJarPath); Assert.assertTrue(file.exists()); - AgentURLClassLoader agentURLClassLoader = new AgentURLClassLoader(new URL[]{file.toURI().toURL()}); - agentURLClassLoader.setBootClass("com.profiler.boot.BootClassTest"); - agentURLClassLoader.boot(); + AgentClassLoader agentClassLoader = new AgentClassLoader(new URL[]{file.toURI().toURL()}); + agentClassLoader.setBootClass("com.profiler.boot.BootClassTest"); + agentClassLoader.boot(); } private String getProjectLibDir() { - ProtectionDomain protectionDomain = AgentURLClassLoader.class.getProtectionDomain(); + ProtectionDomain protectionDomain = AgentClassLoader.class.getProtectionDomain(); CodeSource codeSource = protectionDomain.getCodeSource(); URL location = codeSource.getLocation();