/* * 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.command.*; import io.aeron.logbuffer.FrameDescriptor; import io.aeron.protocol.*; import org.agrona.MutableDirectBuffer; import java.net.InetAddress; import static java.nio.ByteOrder.LITTLE_ENDIAN; import static org.agrona.BitUtil.SIZE_OF_INT; import static org.agrona.BitUtil.SIZE_OF_LONG; /** * Dissect encoded log events. The event consumer of the log should be single threaded. */ public class EventDissector { private static final DataHeaderFlyweight DATA_HEADER = new DataHeaderFlyweight(); private static final StatusMessageFlyweight SM_HEADER = new StatusMessageFlyweight(); private static final NakFlyweight NAK_HEADER = new NakFlyweight(); private static final SetupFlyweight SETUP_HEADER = new SetupFlyweight(); private static final RttMeasurementFlyweight RTT_MEASUREMENT = new RttMeasurementFlyweight(); private static final PublicationMessageFlyweight PUB_MSG = new PublicationMessageFlyweight(); private static final SubscriptionMessageFlyweight SUB_MSG = new SubscriptionMessageFlyweight(); private static final PublicationBuffersReadyFlyweight PUB_READY = new PublicationBuffersReadyFlyweight(); private static final ImageBuffersReadyFlyweight IMAGE_READY = new ImageBuffersReadyFlyweight(); private static final CorrelatedMessageFlyweight CORRELATED_MSG = new CorrelatedMessageFlyweight(); private static final ImageMessageFlyweight IMAGE_MSG = new ImageMessageFlyweight(); private static final RemoveMessageFlyweight REMOVE_MSG = new RemoveMessageFlyweight(); private static final DestinationMessageFlyweight DESTINATION_MSG = new DestinationMessageFlyweight(); public static String dissectAsFrame(final EventCode code, final MutableDirectBuffer buffer, final int offset) { final StringBuilder builder = new StringBuilder(); int relativeOffset = dissectLogHeader(code, buffer, offset, builder); builder.append(": "); relativeOffset += dissectSocketAddress(buffer, offset + relativeOffset, builder); builder.append(" "); final int frameOffset = offset + relativeOffset; switch (frameType(buffer, frameOffset)) { case HeaderFlyweight.HDR_TYPE_PAD: case HeaderFlyweight.HDR_TYPE_DATA: final DataHeaderFlyweight dataFrame = DATA_HEADER; dataFrame.wrap(buffer, frameOffset, buffer.capacity() - frameOffset); builder.append(dissect(dataFrame)); break; case HeaderFlyweight.HDR_TYPE_SM: final StatusMessageFlyweight smFrame = SM_HEADER; smFrame.wrap(buffer, frameOffset, buffer.capacity() - frameOffset); builder.append(dissect(smFrame)); break; case HeaderFlyweight.HDR_TYPE_NAK: final NakFlyweight nakFrame = NAK_HEADER; nakFrame.wrap(buffer, frameOffset, buffer.capacity() - frameOffset); builder.append(dissect(nakFrame)); break; case HeaderFlyweight.HDR_TYPE_SETUP: final SetupFlyweight setupFrame = SETUP_HEADER; setupFrame.wrap(buffer, frameOffset, buffer.capacity() - frameOffset); builder.append(dissect(setupFrame)); break; case HeaderFlyweight.HDR_TYPE_RTTM: final RttMeasurementFlyweight rttMeasurementFlyweight = RTT_MEASUREMENT; rttMeasurementFlyweight.wrap(buffer, frameOffset, buffer.capacity() - frameOffset); builder.append(dissect(rttMeasurementFlyweight)); break; default: builder.append("FRAME_UNKNOWN"); break; } return builder.toString(); } public static String dissectAsCommand(final EventCode code, final MutableDirectBuffer buffer, final int offset) { final StringBuilder builder = new StringBuilder(); final int relativeOffset = dissectLogHeader(code, buffer, offset, builder); builder.append(": "); switch (code) { case CMD_IN_ADD_PUBLICATION: case CMD_IN_ADD_EXCLUSIVE_PUBLICATION: final PublicationMessageFlyweight pubCommand = PUB_MSG; pubCommand.wrap(buffer, offset + relativeOffset); builder.append(dissect(pubCommand)); break; case CMD_IN_ADD_SUBSCRIPTION: final SubscriptionMessageFlyweight subCommand = SUB_MSG; subCommand.wrap(buffer, offset + relativeOffset); builder.append(dissect(subCommand)); break; case CMD_IN_REMOVE_PUBLICATION: case CMD_IN_REMOVE_SUBSCRIPTION: final RemoveMessageFlyweight removeCmd = REMOVE_MSG; removeCmd.wrap(buffer, offset + relativeOffset); builder.append(dissect(removeCmd)); break; case CMD_OUT_PUBLICATION_READY: case CMD_OUT_EXCLUSIVE_PUBLICATION_READY: final PublicationBuffersReadyFlyweight publicationReadyEvent = PUB_READY; publicationReadyEvent.wrap(buffer, offset + relativeOffset); builder.append(dissect(publicationReadyEvent)); break; case CMD_OUT_AVAILABLE_IMAGE: final ImageBuffersReadyFlyweight imageAvailableEvent = IMAGE_READY; imageAvailableEvent.wrap(buffer, offset + relativeOffset); builder.append(dissect(imageAvailableEvent)); break; case CMD_OUT_ON_OPERATION_SUCCESS: case CMD_IN_KEEPALIVE_CLIENT: final CorrelatedMessageFlyweight correlatedEvent = CORRELATED_MSG; correlatedEvent.wrap(buffer, offset + relativeOffset); builder.append(dissect(correlatedEvent)); break; case CMD_OUT_ON_UNAVAILABLE_IMAGE: final ImageMessageFlyweight imageUnavailableEvent = IMAGE_MSG; imageUnavailableEvent.wrap(buffer, offset + relativeOffset); builder.append(dissect(imageUnavailableEvent)); break; case CMD_IN_ADD_DESTINATION: case CMD_IN_REMOVE_DESTINATION: final DestinationMessageFlyweight destinationMessageFlyweight = DESTINATION_MSG; destinationMessageFlyweight.wrap(buffer, offset + relativeOffset); builder.append(dissect(destinationMessageFlyweight)); break; default: builder.append("COMMAND_UNKNOWN"); break; } return builder.toString(); } public static String dissectAsInvocation( final EventCode code, final MutableDirectBuffer buffer, final int initialOffset) { final StringBuilder builder = new StringBuilder(); final int relativeOffset = dissectLogHeader(code, buffer, initialOffset, builder); builder.append(": "); readStackTraceElement(buffer, initialOffset + relativeOffset, builder); return builder.toString(); } public static String dissectAsString(final EventCode code, final MutableDirectBuffer buffer, final int offset) { final StringBuilder builder = new StringBuilder(); final int relativeOffset = dissectLogHeader(code, buffer, offset, builder); builder.append(": "); builder.append(buffer.getStringUtf8(offset + relativeOffset, LITTLE_ENDIAN)); return builder.toString(); } private static int readStackTraceElement( final MutableDirectBuffer buffer, final int offset, final StringBuilder builder) { int i = offset; final int lineNumber = buffer.getInt(i, LITTLE_ENDIAN); i += SIZE_OF_INT; int length = buffer.getInt(i); final String className = buffer.getStringUtf8(i, length); i += SIZE_OF_INT + length; length = buffer.getInt(i); final String methodName = buffer.getStringUtf8(i, length); i += SIZE_OF_INT + length; length = buffer.getInt(i); final String fileName = buffer.getStringUtf8(i, length); i += SIZE_OF_INT + length; builder.append(String.format("%s.%s %s:%d", className, methodName, fileName, lineNumber)); return i; } private static int dissectLogHeader( final EventCode code, final MutableDirectBuffer buffer, final int offset, final StringBuilder builder) { int relativeOffset = 0; final int captureLength = buffer.getInt(offset + relativeOffset, LITTLE_ENDIAN); relativeOffset += SIZE_OF_INT; final int bufferLength = buffer.getInt(offset + relativeOffset, LITTLE_ENDIAN); relativeOffset += SIZE_OF_INT; final long timestamp = buffer.getLong(offset + relativeOffset, LITTLE_ENDIAN); relativeOffset += SIZE_OF_LONG; builder.append(String.format( "[%1$f] %2$s [%3$d/%4$d]", (double)timestamp / 1000000000.0, code.name(), captureLength, bufferLength)); return relativeOffset; } private static int dissectSocketAddress( final MutableDirectBuffer buffer, final int offset, final StringBuilder builder) { int relativeOffset = 0; final int port = buffer.getInt(offset + relativeOffset, LITTLE_ENDIAN); relativeOffset += SIZE_OF_INT; final byte[] addressBuffer = new byte[buffer.getInt(offset + relativeOffset)]; relativeOffset += SIZE_OF_INT; buffer.getBytes(offset + relativeOffset, addressBuffer); relativeOffset += addressBuffer.length; try { builder.append(String.format("%s.%d", InetAddress.getByAddress(addressBuffer).getHostAddress(), port)); } catch (final Exception ex) { ex.printStackTrace(); } return relativeOffset; } private static String dissect(final DataHeaderFlyweight msg) { return String.format( "%s 0x%x len %d %d:%d:%d @%x", msg.headerType() == HeaderFlyweight.HDR_TYPE_PAD ? "PAD" : "DATA", msg.flags(), msg.frameLength(), msg.sessionId(), msg.streamId(), msg.termId(), msg.termOffset()); } private static String dissect(final StatusMessageFlyweight msg) { return String.format( "SM 0x%x len %d %d:%d:%d @%x %d %d", msg.flags(), msg.frameLength(), msg.sessionId(), msg.streamId(), msg.consumptionTermId(), msg.consumptionTermOffset(), msg.receiverWindowLength(), msg.receiverId()); } private static String dissect(final NakFlyweight msg) { return String.format( "NAK 0x%x len %d %d:%d:%d @%x %d", msg.flags(), msg.frameLength(), msg.sessionId(), msg.streamId(), msg.termId(), msg.termOffset(), msg.length()); } private static String dissect(final SetupFlyweight msg) { return String.format( "SETUP 0x%x len %d %d:%d:%d %d @%x %d MTU %d TTL %d", msg.flags(), msg.frameLength(), msg.sessionId(), msg.streamId(), msg.activeTermId(), msg.initialTermId(), msg.termOffset(), msg.termLength(), msg.mtuLength(), msg.ttl()); } private static String dissect(final RttMeasurementFlyweight msg) { return String.format( "RTT 0x%x len %d %d:%d %d %d %d", msg.flags(), msg.frameLength(), msg.sessionId(), msg.streamId(), msg.echoTimestampNs(), msg.receptionDelta(), msg.receiverId()); } private static String dissect(final PublicationMessageFlyweight msg) { return String.format( "%2$s %1$d [%4$d:%3$d]", msg.streamId(), msg.channel(), msg.correlationId(), msg.clientId()); } private static String dissect(final SubscriptionMessageFlyweight msg) { return String.format( "%s %d [%d][%d:%d]", msg.channel(), msg.streamId(), msg.registrationCorrelationId(), msg.clientId(), msg.correlationId()); } private static String dissect(final PublicationBuffersReadyFlyweight msg) { return String.format( "%d:%d %d [%d]%n %s", msg.sessionId(), msg.streamId(), msg.publicationLimitCounterId(), msg.correlationId(), msg.logFileName()); } private static String dissect(final ImageBuffersReadyFlyweight msg) { final StringBuilder positions = new StringBuilder(); for (int i = 0; i < msg.subscriberPositionCount(); i++) { positions.append(String.format( "[%d:%d:%d]", i, msg.subscriberPositionId(i), msg.positionIndicatorRegistrationId(i))); } return String.format( "%d:%d %s \"%s\" [%d]%n %s", msg.sessionId(), msg.streamId(), positions.toString(), msg.sourceIdentity(), msg.correlationId(), msg.logFileName()); } private static String dissect(final CorrelatedMessageFlyweight msg) { return String.format( "[%d:%d]", msg.clientId(), msg.correlationId()); } private static String dissect(final ImageMessageFlyweight msg) { return String.format( "%s %d [%d]", msg.channel(), msg.streamId(), msg.correlationId()); } private static String dissect(final RemoveMessageFlyweight msg) { return String.format( "%d [%d:%d]", msg.registrationId(), msg.clientId(), msg.correlationId()); } private static String dissect(final DestinationMessageFlyweight msg) { return String.format( "%s %d [%d:%d]", msg.channel(), msg.registrationCorrelationId(), msg.clientId(), msg.correlationId()); } public static int frameType(final MutableDirectBuffer buffer, final int termOffset) { return buffer.getShort(FrameDescriptor.typeOffset(termOffset), LITTLE_ENDIAN) & 0xFFFF; } }