Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gracefully shutdown the agent if it encounters issues on startup #1136

Merged
merged 3 commits into from
Mar 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions newrelic-agent/src/main/java/com/newrelic/agent/Agent.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
import com.newrelic.agent.service.ServiceManagerImpl;
import com.newrelic.agent.stats.StatsService;
import com.newrelic.agent.stats.StatsWorks;
import com.newrelic.agent.util.UnwindableInstrumentation;
import com.newrelic.agent.util.UnwindableInstrumentationImpl;
import com.newrelic.agent.util.asm.ClassStructure;
import com.newrelic.bootstrap.BootstrapAgent;
import com.newrelic.bootstrap.BootstrapLoader;
import com.newrelic.weave.utils.Streams;
import org.objectweb.asm.ClassReader;
Expand Down Expand Up @@ -119,6 +122,7 @@ public static void disableFastPath() {
*/
@SuppressWarnings("unused")
public static void continuePremain(String agentArgs, Instrumentation inst, long startTime) {
inst = maybeWrapInstrumentation(inst);
final LifecycleObserver lifecycleObserver = LifecycleObserver.createLifecycleObserver(agentArgs);
if (!lifecycleObserver.isAgentSafe()) {
return;
Expand Down Expand Up @@ -152,8 +156,9 @@ public static void continuePremain(String agentArgs, Instrumentation inst, long
return;
}

ServiceManager serviceManager = null;
try {
ServiceManager serviceManager = ServiceFactory.getServiceManager();
serviceManager = ServiceFactory.getServiceManager();

// The following method will immediately configure the log so that the rest of our initialization sequence
// is written to the newrelic_agent.log rather than to the console. Configuring the log also applies the
Expand Down Expand Up @@ -199,11 +204,38 @@ public static void continuePremain(String agentArgs, Instrumentation inst, long
}
}
t.printStackTrace();
System.exit(1);

if (inst instanceof UnwindableInstrumentation) {
final UnwindableInstrumentation instrumentation = (UnwindableInstrumentation) inst;
if (serviceManager != null) {
try {
serviceManager.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
LOG.severe("Detaching the New Relic agent");
instrumentation.unwind();
LOG.severe("The New Relic agent was detached");
return;
} else {
System.exit(1);
}
}
if (inst instanceof UnwindableInstrumentation) {
final UnwindableInstrumentation instrumentation = (UnwindableInstrumentation) inst;
instrumentation.started();
}
lifecycleObserver.agentStarted();
}

private static Instrumentation maybeWrapInstrumentation(Instrumentation inst) {
if (System.getProperty(BootstrapAgent.NR_AGENT_ARGS_SYSTEM_PROPERTY) != null) {
return UnwindableInstrumentationImpl.wrapInstrumentation(inst);
}
return inst;
}

private static boolean tryToInitializeServiceManager(Instrumentation inst) {
try {
CoreService coreService = new CoreServiceImpl(inst);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.newrelic.agent.util;

import java.lang.instrument.ClassDefinition;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.util.jar.JarFile;

public class DelegatingInstrumentation implements Instrumentation {
protected final Instrumentation delegate;

public DelegatingInstrumentation(Instrumentation instrumentation) {
this.delegate = instrumentation;
}

@Override
public void addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
delegate.addTransformer(transformer, canRetransform);
}

@Override
public void addTransformer(ClassFileTransformer transformer) {
delegate.addTransformer(transformer);
}

@Override
public boolean removeTransformer(ClassFileTransformer transformer) {
return delegate.removeTransformer(transformer);
}

@Override
public boolean isRetransformClassesSupported() {
return delegate.isRetransformClassesSupported();
}

@Override
public void retransformClasses(Class<?>... classes) throws UnmodifiableClassException {
delegate.retransformClasses(classes);
}

@Override
public boolean isRedefineClassesSupported() {
return delegate.isRedefineClassesSupported();
}

@Override
public void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException {
delegate.redefineClasses(definitions);
}

@Override
public boolean isModifiableClass(Class<?> theClass) {
return delegate.isModifiableClass(theClass);
}

@Override
public Class[] getAllLoadedClasses() {
return delegate.getAllLoadedClasses();
}

@Override
public Class[] getInitiatedClasses(ClassLoader loader) {
return delegate.getInitiatedClasses(loader);
}

@Override
public long getObjectSize(Object objectToSize) {
return delegate.getObjectSize(objectToSize);
}

@Override
public void appendToBootstrapClassLoaderSearch(JarFile jarfile) {
delegate.appendToBootstrapClassLoaderSearch(jarfile);
}

@Override
public void appendToSystemClassLoaderSearch(JarFile jarfile) {
delegate.appendToSystemClassLoaderSearch(jarfile);
}

@Override
public boolean isNativeMethodPrefixSupported() {
return delegate.isNativeMethodPrefixSupported();
}

@Override
public void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
delegate.setNativeMethodPrefix(transformer, prefix);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,13 @@
/**
* This delegating wrapper of an {@link Instrumentation} instance.
*/
public class InstrumentationWrapper implements Instrumentation {
protected final Instrumentation delegate;
public class InstrumentationWrapper extends DelegatingInstrumentation {

public InstrumentationWrapper(Instrumentation delegate) {
super();
this.delegate = delegate;
}

public void addTransformer(ClassFileTransformer transformer, boolean canRetransform) {
delegate.addTransformer(transformer, canRetransform);
}

public void addTransformer(ClassFileTransformer transformer) {
delegate.addTransformer(transformer);
}

public boolean removeTransformer(ClassFileTransformer transformer) {
return delegate.removeTransformer(transformer);
}

public boolean isRetransformClassesSupported() {
return delegate.isRetransformClassesSupported();
super(delegate);
}

@Override
public void retransformClasses(Class<?>... classes) throws UnmodifiableClassException {
if (Agent.LOG.isFinestEnabled()) {
StringBuilder sb = new StringBuilder("Classes about to be retransformed: ");
Expand All @@ -51,13 +34,10 @@ public void retransformClasses(Class<?>... classes) throws UnmodifiableClassExce
}
Agent.LOG.log(Level.FINEST, sb.toString());
}
delegate.retransformClasses(classes);
}

public boolean isRedefineClassesSupported() {
return delegate.isRedefineClassesSupported();
super.retransformClasses(classes);
}

@Override
public void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException,
UnmodifiableClassException {
if (Agent.LOG.isFinestEnabled()) {
Expand All @@ -69,39 +49,4 @@ public void redefineClasses(ClassDefinition... definitions) throws ClassNotFound
}
delegate.redefineClasses(definitions);
}

public boolean isModifiableClass(Class<?> theClass) {
return delegate.isModifiableClass(theClass);
}

@SuppressWarnings("rawtypes")
public Class[] getAllLoadedClasses() {
return delegate.getAllLoadedClasses();
}

@SuppressWarnings("rawtypes")
public Class[] getInitiatedClasses(ClassLoader loader) {
return delegate.getInitiatedClasses(loader);
}

public long getObjectSize(Object objectToSize) {
return delegate.getObjectSize(objectToSize);
}

public void appendToBootstrapClassLoaderSearch(JarFile jarfile) {
delegate.appendToBootstrapClassLoaderSearch(jarfile);
}

public void appendToSystemClassLoaderSearch(JarFile jarfile) {
delegate.appendToSystemClassLoaderSearch(jarfile);
}

public boolean isNativeMethodPrefixSupported() {
return delegate.isNativeMethodPrefixSupported();
}

public void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix) {
delegate.setNativeMethodPrefix(transformer, prefix);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.newrelic.agent.util;

import java.lang.instrument.Instrumentation;

public interface UnwindableInstrumentation extends Instrumentation {
/**
* Remove all New Relic class transformers and rejit any classes they have modified
*/
void unwind();

void started();
}
Loading