/*
* Copyright 2014-2017 Real Logic Ltd.
*
* 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 io.aeron.agent;
import io.aeron.driver.EventLog;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.utility.JavaModule;
import org.agrona.concurrent.AgentRunner;
import org.agrona.concurrent.SleepingIdleStrategy;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.util.concurrent.TimeUnit;
import static net.bytebuddy.asm.Advice.to;
import static net.bytebuddy.matcher.ElementMatchers.*;
public class EventLogAgent
{
private static final long SLEEP_PERIOD_NS = TimeUnit.MILLISECONDS.toNanos(1);
private static final EventLogReaderAgent EVENT_LOG_READER_AGENT = new EventLogReaderAgent();
private static final AgentRunner EVENT_LOG_READER_AGENT_RUNNER = new AgentRunner(
new SleepingIdleStrategy(SLEEP_PERIOD_NS),
EventLogAgent::errorHandler,
null,
EVENT_LOG_READER_AGENT);
private static final Thread EVENT_LOG_READER_THREAD = new Thread(EVENT_LOG_READER_AGENT_RUNNER);
private static volatile ClassFileTransformer logTransformer;
private static volatile Instrumentation instrumentation;
private static final AgentBuilder.Listener LISTENER = new AgentBuilder.Listener()
{
public void onTransformation(
final TypeDescription typeDescription,
final ClassLoader classLoader,
final JavaModule module,
final boolean loaded,
final DynamicType dynamicType)
{
System.out.format("TRANSFORM %s%n", typeDescription.getName());
}
public void onIgnored(
final TypeDescription typeDescription,
final ClassLoader classLoader,
final JavaModule module,
final boolean loaded)
{
}
public void onError(
final String typeName,
final ClassLoader classLoader,
final JavaModule module,
final boolean loaded,
final Throwable throwable)
{
System.out.format("ERROR %s%n", typeName);
throwable.printStackTrace(System.out);
}
public void onComplete(
final String typeName, final ClassLoader classLoader, final JavaModule module, final boolean loaded)
{
}
};
private static void errorHandler(final Throwable throwable)
{
}
private static void agent(final boolean shouldRedefine, final Instrumentation instrumentation)
{
if (EventConfiguration.ENABLED_EVENT_CODES == 0)
{
return;
}
/*
* Intercept based on enabled events:
* SenderProxy
* ReceiverProxy
* ClientProxy
* DriverConductor (onClientCommand)
* SendChannelEndpoint
* ReceiveChannelEndpoint
*/
EventLogAgent.instrumentation = instrumentation;
logTransformer = new AgentBuilder.Default(new ByteBuddy().with(TypeValidation.DISABLED))
.with(LISTENER)
.disableClassFormatChanges()
.with(shouldRedefine ?
AgentBuilder.RedefinitionStrategy.RETRANSFORMATION :
AgentBuilder.RedefinitionStrategy.DISABLED)
.type(nameEndsWith("DriverConductor"))
.transform((builder, typeDescription, classLoader, javaModule) ->
builder
.visit(to(CleanupInterceptor.DriverConductorInterceptor.CleanupImage.class)
.on(named("cleanupImage")))
.visit(to(CleanupInterceptor.DriverConductorInterceptor.CleanupPublication.class)
.on(named("cleanupPublication")))
.visit(to(CleanupInterceptor.DriverConductorInterceptor.CleanupSubscriptionLink.class)
.on(named("cleanupSubscriptionLink"))))
.type(nameEndsWith("DriverAdapter"))
.transform((builder, typeDescription, classLoader, javaModule) ->
builder
.visit(to(CmdInterceptor.class).on(named("onMessage"))))
.type(nameEndsWith("ClientProxy"))
.transform((builder, typeDescription, classLoader, javaModule) ->
builder.visit(to(CmdInterceptor.class).on(named("transmit"))))
.type(nameEndsWith("SenderProxy"))
.transform((builder, typeDescription, classLoader, javaModule) ->
builder
.visit(to(ChannelEndpointInterceptor.SenderProxyInterceptor.RegisterSendChannelEndpoint.class)
.on(named("registerSendChannelEndpoint")))
.visit(to(ChannelEndpointInterceptor.SenderProxyInterceptor.CloseSendChannelEndpoint.class)
.on(named("closeSendChannelEndpoint"))))
.type(nameEndsWith("ReceiverProxy"))
.transform((builder, typeDescription, classLoader, javaModule) ->
builder
.visit(to(ChannelEndpointInterceptor.ReceiverProxyInterceptor.RegisterReceiveChannelEndpoint.class)
.on(named("registerReceiveChannelEndpoint")))
.visit(to(ChannelEndpointInterceptor.ReceiverProxyInterceptor.CloseReceiveChannelEndpoint.class)
.on(named("closeReceiveChannelEndpoint"))))
.type(inheritsAnnotation(EventLog.class))
.transform((builder, typeDescription, classLoader, javaModule) ->
builder
.visit(to(ChannelEndpointInterceptor.SendChannelEndpointInterceptor.Presend.class)
.on(named("presend")))
.visit(to(ChannelEndpointInterceptor.ReceiveChannelEndpointInterceptor.SendTo.class)
.on(named("sendTo")))
.visit(to(ChannelEndpointInterceptor.SendChannelEndpointInterceptor.OnStatusMessage.class)
.on(named("onStatusMessage")))
.visit(to(ChannelEndpointInterceptor.SendChannelEndpointInterceptor.OnNakMessage.class)
.on(named("onNakMessage")))
.visit(to(ChannelEndpointInterceptor.SendChannelEndpointInterceptor.OnRttMeasurement.class)
.on(named("onRttMeasurement")))
.visit(to(ChannelEndpointInterceptor.ReceiveChannelEndpointInterceptor.OnDataPacket.class)
.on(named("onDataPacket")))
.visit(to(ChannelEndpointInterceptor.ReceiveChannelEndpointInterceptor.OnSetupMessage.class)
.on(named("onSetupMessage")))
.visit(to(ChannelEndpointInterceptor.ReceiveChannelEndpointInterceptor.OnRttMeasurement.class)
.on(named("onRttMeasurement"))))
.installOn(instrumentation);
EVENT_LOG_READER_THREAD.setName("event log reader");
EVENT_LOG_READER_THREAD.setDaemon(true);
EVENT_LOG_READER_THREAD.start();
}
public static void premain(final String agentArgs, final Instrumentation instrumentation)
{
agent(false, instrumentation);
}
public static void agentmain(final String agentArgs, final Instrumentation instrumentation)
{
agent(true, instrumentation);
}
public static void removeTransformer()
{
if (logTransformer != null)
{
instrumentation.removeTransformer(logTransformer);
instrumentation.removeTransformer(new AgentBuilder.Default()
.type(nameEndsWith("DriverConductor")
.or(nameEndsWith("ClientProxy"))
.or(nameEndsWith("SenderProxy"))
.or(nameEndsWith("ReceiverProxy"))
.or(inheritsAnnotation(EventLog.class)))
.transform(AgentBuilder.Transformer.NoOp.INSTANCE)
.installOn(instrumentation));
}
}
}