/* * 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.samples; import io.aeron.LogBuffers; import io.aeron.logbuffer.FrameDescriptor; import io.aeron.logbuffer.LogBufferDescriptor; import io.aeron.protocol.DataHeaderFlyweight; import org.agrona.BitUtil; import org.agrona.DirectBuffer; import org.agrona.concurrent.UnsafeBuffer; import java.io.PrintStream; import java.util.Date; import static io.aeron.logbuffer.LogBufferDescriptor.*; import static io.aeron.protocol.DataHeaderFlyweight.HEADER_LENGTH; import static java.lang.Math.min; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; /** * Command line utility for inspecting a log buffer to see what terms and messages it contains. */ public class LogInspector { private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); private static final String DATA_FORMAT = System.getProperty( "aeron.log.inspector.data.format", "hex").toLowerCase(); private static final boolean SKIP_DEFAULT_HEADER = Boolean.getBoolean("aeron.log.inspector.skipDefaultHeader"); private static final boolean SCAN_OVER_ZEROES = Boolean.getBoolean("aeron.log.inspector.scanOverZeroes"); public static void main(final String[] args) throws Exception { final PrintStream out = System.out; if (args.length < 1) { out.println("Usage: LogInspector <logFileName> [message dump limit]"); return; } final String logFileName = args[0]; final int messageDumpLimit = args.length >= 2 ? Integer.parseInt(args[1]) : Integer.MAX_VALUE; try (LogBuffers logBuffers = new LogBuffers(logFileName, READ_ONLY)) { out.println("======================================================================"); out.format("%s Inspection dump for %s%n", new Date(), logFileName); out.println("======================================================================"); final DataHeaderFlyweight dataHeaderFlyweight = new DataHeaderFlyweight(); final UnsafeBuffer[] termBuffers = logBuffers.termBuffers(); final int termLength = logBuffers.termLength(); final UnsafeBuffer metaDataBuffer = logBuffers.metaDataBuffer(); final int initialTermId = initialTermId(metaDataBuffer); out.format("Time of last SM: %s%n", new Date(timeOfLastStatusMessage(metaDataBuffer))); out.format("Initial term id: %d%n", initialTermId); out.format(" Active index: %d%n", activePartitionIndex(metaDataBuffer)); out.format(" Term length: %d%n", termLength); out.format(" MTU length: %d%n%n", mtuLength(metaDataBuffer)); if (!SKIP_DEFAULT_HEADER) { dataHeaderFlyweight.wrap(defaultFrameHeader(metaDataBuffer)); out.format("default %s%n", dataHeaderFlyweight); } out.println(); for (int i = 0; i < PARTITION_COUNT; i++) { final long rawTail = rawTailVolatile(metaDataBuffer, 0); final long termOffset = rawTail & 0xFFFF_FFFFL; final int termId = termId(rawTail); final int offset = (int)Math.min(termOffset, termLength); final int bitsToShift = Integer.numberOfTrailingZeros(termLength); out.format( "Index %d Term Meta Data termOffset=%d termId=%d rawTail=%d position=%d%n", i, termOffset, termId, rawTail, LogBufferDescriptor.computePosition(termId, offset, bitsToShift, initialTermId) ); } for (int i = 0; i < PARTITION_COUNT; i++) { out.println(); out.println("======================================================================"); out.format("Index %d Term Data%n%n", i); final UnsafeBuffer termBuffer = termBuffers[i]; int offset = 0; do { dataHeaderFlyweight.wrap(termBuffer, offset, termLength - offset); out.println(offset + ": " + dataHeaderFlyweight.toString()); final int frameLength = dataHeaderFlyweight.frameLength(); if (frameLength < DataHeaderFlyweight.HEADER_LENGTH) { if (0 == frameLength && SCAN_OVER_ZEROES) { offset += FrameDescriptor.FRAME_ALIGNMENT; continue; } try { final int limit = min(termLength - (offset + HEADER_LENGTH), messageDumpLimit); out.println(formatBytes(termBuffer, offset + HEADER_LENGTH, limit)); } catch (final Exception ex) { System.err.printf("frameLength=%d offset=%d%n", frameLength, offset); ex.printStackTrace(); } break; } final int limit = min(frameLength - HEADER_LENGTH, messageDumpLimit); out.println(formatBytes(termBuffer, offset + HEADER_LENGTH, limit)); offset += BitUtil.align(frameLength, FrameDescriptor.FRAME_ALIGNMENT); } while (offset < termLength); } } } public static char[] formatBytes(final DirectBuffer buffer, final int offset, final int length) { switch (DATA_FORMAT) { case "ascii": return bytesToAscii(buffer, offset, length); default: return bytesToHex(buffer, offset, length); } } private static char[] bytesToAscii(final DirectBuffer buffer, final int offset, final int length) { final char[] chars = new char[length]; for (int i = 0; i < length; i++) { byte b = buffer.getByte(offset + i); if (b < 0) { b = 0; } chars[i] = (char)b; } return chars; } public static char[] bytesToHex(final DirectBuffer buffer, final int offset, final int length) { final char[] chars = new char[length * 2]; for (int i = 0; i < length; i++) { final int b = buffer.getByte(offset + i) & 0xFF; chars[i * 2] = HEX_ARRAY[b >>> 4]; chars[i * 2 + 1] = HEX_ARRAY[b & 0x0F]; } return chars; } }