/**
* Copyright 2005-2012 Akiban Technologies, Inc.
*
* 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 com.persistit;
import static com.persistit.JournalRecord.OVERHEAD;
import static com.persistit.JournalRecord.SUB_RECORD_OVERHEAD;
import static com.persistit.JournalRecord.getLength;
import static com.persistit.JournalRecord.getTimestamp;
import static com.persistit.JournalRecord.getType;
import static com.persistit.JournalRecord.isValidType;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.BitSet;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.persistit.JournalRecord.CP;
import com.persistit.JournalRecord.D0;
import com.persistit.JournalRecord.D1;
import com.persistit.JournalRecord.DR;
import com.persistit.JournalRecord.DT;
import com.persistit.JournalRecord.IT;
import com.persistit.JournalRecord.IV;
import com.persistit.JournalRecord.JE;
import com.persistit.JournalRecord.JH;
import com.persistit.JournalRecord.PA;
import com.persistit.JournalRecord.PM;
import com.persistit.JournalRecord.SR;
import com.persistit.JournalRecord.TM;
import com.persistit.JournalRecord.TX;
import com.persistit.exception.CorruptJournalException;
import com.persistit.exception.PersistitException;
import com.persistit.exception.PersistitIOException;
import com.persistit.mxbeans.JournalManagerMXBean;
import com.persistit.util.ArgParser;
/**
* @author Peter Beaman
* @version 1.0
*/
class JournalTool {
public final static int DEFAULT_BUFFER_SIZE = 1 * 1024 * 1024;
public final static long DEFAULT_BLOCK_SIZE = 1000000000L;
private final static int EOF = -1;
private final static int EOJ = -2;
final static String[] ARGS_TEMPLATE = { "path|string:|Journal file name",
"start|long:0:0:10000000000000|Start journal address",
"end|long:1000000000000000000:0:1000000000000000000|End journal address",
"types|String:*|Selected record types, for example, \"PA,PM,CP\"",
"pages|String:*|Selected pages, for example, \"0,1,200-299,33333-\"",
"timestamps|String:*|Selected timestamps, for example, \"132466-132499\"",
"maxkey|int:42:4:10000|Maximum displayed key length",
"maxvalue|int:42:4:100000|Maximum displayed value length",
"timestamps|String:*|Selected timestamps, for example, \"132466-132499\"",
"_flag|v|Verbose dump - includes PageMap and TransactionMap details" };
private final static SimpleDateFormat SDF = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
private final Persistit _persistit;
private Action _action;
private String _journalFilePath;
private long _startAddr;
private long _endAddr;
private PrintWriter _writer = new PrintWriter(new OutputStreamWriter(System.out));
private final Map<Long, FileChannel> _journalFileChannels = new HashMap<Long, FileChannel>();
private ByteBuffer _readBuffer;
private final int _readBufferSize = DEFAULT_BUFFER_SIZE;
private long _readBufferAddress;
private long _currentAddress;
private long _blockSize = DEFAULT_BLOCK_SIZE;
private BitSet _selectedTypes = new BitSet(65536);
private RangePredicate _selectedPages;
private RangePredicate _selectedTimestamps;
private int _keyDisplayLength;
private int _valueDisplayLength;
private boolean _verbose;
public long getStartAddr() {
return _startAddr;
}
public void setStartAddr(final long startAddr) {
_startAddr = startAddr;
}
public long getEndAddr() {
return _endAddr;
}
public void setEndAddr(final long endAddr) {
_endAddr = endAddr;
}
public Action getAction() {
return _action;
}
public void setAction(final Action action) {
_action = action;
}
public PrintWriter getWriter() {
return _writer;
}
public void setWriter(final PrintWriter writer) {
_writer = writer;
}
public BitSet getSelectedTypes() {
return _selectedTypes;
}
public void setSelectedTypes(final BitSet selectedTypes) {
_selectedTypes = selectedTypes;
}
public RangePredicate getSelectedPages() {
return _selectedPages;
}
public void setSelectedPages(final RangePredicate selectedPages) {
_selectedPages = selectedPages;
}
public RangePredicate getSelectedTimestamps() {
return _selectedTimestamps;
}
public void setSelectedTimestamps(final RangePredicate selectedTimestamps) {
_selectedTimestamps = selectedTimestamps;
}
public int getKeyDisplayLength() {
return _keyDisplayLength;
}
public void setKeyDisplayLength(final int keyDisplayLength) {
_keyDisplayLength = keyDisplayLength;
}
public int getValueDisplayLength() {
return _valueDisplayLength;
}
public void setValueDisplayLength(final int valueDisplayLength) {
_valueDisplayLength = valueDisplayLength;
}
public boolean isVerbose() {
return _verbose;
}
public void setVerbose(final boolean verbose) {
_verbose = verbose;
}
public interface Action {
public void je(final long address, final long timestamp, final int recordSize) throws Exception;
public void jh(final long address, final long timestamp, final int recordSize) throws Exception;
public void iv(final long address, final long timestamp, final int recordSize) throws Exception;
public void it(final long address, final long timestamp, final int recordSize) throws Exception;
public void cp(final long address, final long timestamp, final int recordSize) throws Exception;
public void tx(final long address, final long timestamp, final int recordSize) throws Exception;
public void sr(final long address, final long timestamp, final int recordSize) throws Exception;
public void dr(final long address, final long timestamp, final int recordSize) throws Exception;
public void dt(final long address, final long timestamp, final int recordSize) throws Exception;
public void pa(final long address, final long timestamp, final int recordSize) throws Exception;
public void pm(final long address, final long timestamp, final int recordSize) throws Exception;
public void tm(final long address, final long timestamp, final int recordSize) throws Exception;
public void d0(final long address, final long timestamp, final int recordSize) throws Exception;
public void d1(final long address, final long timestamp, final int recordSize) throws Exception;
public void eof(final long address) throws Exception;
}
protected static class RangePredicate {
private final long[] _left;
private final long[] _right;
RangePredicate(final String ps) {
if ("*".equals(ps)) {
_left = new long[0];
_right = new long[0];
} else {
final String[] terms = ps.split(",");
_left = new long[terms.length];
_right = new long[terms.length];
for (int index = 0; index < terms.length; index++) {
final String[] range = terms[index].split("-");
boolean okay = true;
try {
switch (range.length) {
case 1:
final long v = Long.parseLong(range[0]);
_left[index] = v;
_right[index] = v;
break;
case 2:
if (range[0].isEmpty()) {
_left[index] = Long.MIN_VALUE;
} else {
_left[index] = Long.parseLong(range[0]);
}
if (range[1].isEmpty()) {
_right[index] = Long.MAX_VALUE;
} else {
_right[index] = Long.parseLong(range[1]);
}
break;
default:
okay = false;
}
} catch (final NumberFormatException e) {
okay = false;
}
if (!okay) {
throw new IllegalArgumentException("Invalid term " + terms[index] + " in range specification "
+ ps);
}
}
}
}
private boolean isSelected(final long v) {
if (_left.length == 0) {
return true;
}
for (int index = 0; index < _left.length; index++) {
if (_left[index] <= v && _right[index] >= v) {
return true;
}
}
return false;
}
}
public JournalTool(final Persistit persistit) {
_persistit = persistit;
_action = new SimpleDumpAction();
_selectedTypes.set(0, 65535, true);
}
public void init(final String[] args) {
final ArgParser ap = new ArgParser("com.persistit.JournalTool", args, ARGS_TEMPLATE).strict();
if (ap.isUsageOnly()) {
return;
}
init(ap);
}
void init(final ArgParser ap) {
init(ap.getStringValue("path"), ap.getLongValue("start"), ap.getLongValue("end"), ap.getStringValue("types"),
ap.getStringValue("pages"), ap.getStringValue("timestamps"), ap.getIntValue("maxkey"),
ap.getIntValue("maxvalue"), ap.isFlag('v'));
}
void init(final String path, final long start, final long end, final String types, final String pages,
final String timestamps, final int maxkey, final int maxvalue, final boolean v) {
_startAddr = start;
_endAddr = end;
String pathName = path;
if (isNullOrEmpty(pathName) && _persistit != null) {
pathName = _persistit.getJournalManager().getJournalFilePath();
}
if (isNullOrEmpty(pathName)) {
throw new IllegalArgumentException(
"The 'path' parameter must specify a valid journal path, for example, \n"
+ " /xxx/yyy/jjj where journal file names " + "are like jjj.000000001234");
}
final long generation = JournalManager.fileToGeneration(new File(pathName));
if (generation != -1) {
pathName = pathName.substring(0, pathName.lastIndexOf('.'));
if (start == 0) {
_startAddr = generation * JournalManagerMXBean.DEFAULT_BLOCK_SIZE;
}
if (end == 1000000000000000000l) {
_endAddr = (generation + 1) * JournalManagerMXBean.DEFAULT_BLOCK_SIZE;
}
}
_journalFilePath = pathName;
_readBuffer = ByteBuffer.allocate(_readBufferSize);
parseTypes(types);
_selectedPages = new RangePredicate(pages);
_selectedTimestamps = new RangePredicate(timestamps);
_keyDisplayLength = maxkey;
_valueDisplayLength = maxvalue;
_verbose = v;
}
private boolean isNullOrEmpty(final String string) {
return string == null || string.isEmpty();
}
private void parseTypes(final String types) {
boolean okay = true;
if (!("*".equals(types))) {
_selectedTypes.set(0, _selectedTypes.size(), false);
for (final String typeString : types.split(",")) {
if (typeString.length() == 2) {
final int t = (typeString.charAt(0) << 8) | typeString.charAt(1);
if (JournalRecord.isValidType(t)) {
_selectedTypes.set(t, true);
} else {
okay = false;
}
} else {
okay = false;
}
}
}
if (!okay) {
throw new IllegalArgumentException("The 'types' parameter must specify either \"*\" or a "
+ "comma-separated list of valid record type names");
}
}
public void scan() throws Exception {
try {
_currentAddress = _startAddr;
_readBufferAddress = Long.MIN_VALUE;
while (_currentAddress < _endAddr) {
final int type = scanOneRecord();
switch (type) {
case JE.TYPE:
case EOF:
_currentAddress = addressUp(_currentAddress);
break;
case EOJ:
return;
}
}
} finally {
_writer.flush();
}
}
/**
* Attempts to read and apply the record at _currentAddress. If it finds
* valid record contained in the current journal file, it advances the
* _currentAddress to the start of the next record and returns the type of
* the record. Otherwise this method does nothing and returns -1;
*
* @return The record type: one of the type values specified in
* {@link com.persistit.JournalRecord}), 0 if the journal file has
* fewer than 16 bytes remaining or -t where t is an invalid type.
* @throws CorruptJournalException
* @throws PersistitException
* @throws JournalNotClosedException
*/
private int scanOneRecord() throws Exception {
final long from = _currentAddress;
try {
read(_currentAddress, OVERHEAD);
} catch (final CorruptJournalException e) {
// This exception means abnormal end-of-file.
_action.eof(from);
return EOF;
} catch (final PersistitIOException e) {
if (e.getCause() instanceof FileNotFoundException) {
return EOJ;
} else {
throw e;
}
}
final int recordSize = getLength(_readBuffer);
final int type = getType(_readBuffer);
final long timestamp = getTimestamp(_readBuffer);
_currentAddress = processOneRecord(from, timestamp, recordSize, type);
return type;
}
long processOneRecord(final long from, final long timestamp, final int recordSize, final int type) throws Exception {
if (recordSize >= _blockSize || recordSize < SUB_RECORD_OVERHEAD) {
throw new CorruptJournalException("Bad JournalRecord length " + recordSize + " at position "
+ addressToString(from, timestamp));
}
long address = from;
switch (type) {
case JE.TYPE:
if (_selectedTypes.get(type)) {
_action.je(address, timestamp, recordSize);
}
break;
case JH.TYPE:
read(_currentAddress, recordSize);
final long blockSize = JH.getBlockSize(_readBuffer);
if (blockSize != _blockSize) {
address = _currentAddress = (_currentAddress / _blockSize) * blockSize;
_readBufferAddress = _currentAddress;
_blockSize = blockSize;
}
if (_selectedTypes.get(type)) {
_action.jh(address, timestamp, recordSize);
}
break;
case SR.TYPE:
if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
_action.sr(address, timestamp, recordSize);
}
break;
case DR.TYPE:
if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
_action.dr(address, timestamp, recordSize);
}
break;
case DT.TYPE:
if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
_action.dt(address, timestamp, recordSize);
}
break;
case IV.TYPE:
if (_selectedTypes.get(type)) {
_action.iv(address, timestamp, recordSize);
}
break;
case IT.TYPE:
if (_selectedTypes.get(type)) {
_action.it(address, timestamp, recordSize);
}
break;
case PA.TYPE:
if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
_action.pa(address, timestamp, recordSize);
}
break;
case PM.TYPE:
if (_selectedTypes.get(type)) {
_action.pm(address, timestamp, recordSize);
}
break;
case TM.TYPE:
if (_selectedTypes.get(type)) {
_action.tm(address, timestamp, recordSize);
}
break;
case TX.TYPE:
if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
_action.tx(address, timestamp, recordSize);
}
break;
case CP.TYPE:
if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
_action.cp(address, timestamp, recordSize);
}
break;
case D0.TYPE:
if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
_action.d0(address, timestamp, recordSize);
}
break;
case D1.TYPE:
if (_selectedTypes.get(type) && _selectedTimestamps.isSelected(timestamp)) {
_action.d1(address, timestamp, recordSize);
}
break;
default:
if (!isValidType(type)) {
_currentAddress -= OVERHEAD;
throw new CorruptJournalException("Invalid record type " + type + " at " + addressToString(address));
}
}
return address + recordSize;
}
private long addressUp(final long address) {
return ((address / _blockSize) + 1) * _blockSize;
}
private synchronized FileChannel getFileChannel(final long address) throws PersistitIOException {
final long generation = address / _blockSize;
FileChannel channel = _journalFileChannels.get(generation);
if (channel == null) {
try {
final RandomAccessFile raf = new RandomAccessFile(addressToFile(address), "r");
channel = raf.getChannel();
_journalFileChannels.put(generation, channel);
} catch (final IOException ioe) {
throw new PersistitIOException(ioe);
}
}
return channel;
}
File addressToFile(final long address) {
return JournalManager.generationToFile(_journalFilePath, address / _blockSize);
}
private void read(final long address, final int size) throws PersistitIOException {
if (_readBufferAddress >= 0 && address >= _readBufferAddress
&& size + address - _readBufferAddress <= _readBuffer.limit()) {
_readBuffer.position((int) (address - _readBufferAddress));
} else {
try {
final FileChannel fc = getFileChannel(address);
_readBuffer.clear();
int maxSize = _readBuffer.capacity();
final long remainingInBlock = addressUp(address) - address;
if (remainingInBlock < maxSize) {
maxSize = (int) remainingInBlock;
}
_readBuffer.limit(maxSize);
// Try to read up to maxSize bytes into _readBuffer
int offset = 0;
while (_readBuffer.remaining() > 0) {
final int readSize = fc.read(_readBuffer, offset + address % _blockSize);
if (readSize < 0) {
break;
}
offset += readSize;
}
_readBufferAddress = address;
_readBuffer.flip();
if (_readBuffer.remaining() < size) {
throw new CorruptJournalException("End of file at " + addressToString(address));
}
} catch (final IOException e) {
throw new PersistitIOException("Reading from " + addressToString(address), e);
}
}
}
private String addressToString(final long address) {
return String.format("JournalAddress %,d", address);
}
private String addressToString(final long address, final long timestamp) {
return String.format("JournalAddress %,d{%,d}", address, timestamp);
}
// -----------------------
public static void main(final String[] args) throws Exception {
final Persistit persistit = new Persistit();
JournalTool jt = null;
try {
jt = new JournalTool(persistit);
jt.init(args);
} catch (final IllegalArgumentException e) {
System.err.println(e.getLocalizedMessage());
System.exit(1);
}
try {
jt.scan();
} catch (final Exception e) {
e.printStackTrace();
} finally {
persistit.close();
}
}
protected class SimpleDumpAction implements Action {
final static String ELIPSES = "...";
final StringBuilder sb = new StringBuilder();
final Key key1 = new Key(_persistit);
final Key key2 = new Key(_persistit);
final Value value = new Value(_persistit);
protected void start(final long address, final long timestamp, final String type, final int recordSize) {
sb.setLength(0);
sb.append(String.format("%,18d%,16d %2s (%8d) ", address, timestamp, type, recordSize));
}
protected void appendf(final String format, final Object... args) {
sb.append(String.format(format, args));
}
protected void keyf(final Key key) {
String s = null;
try {
s = key.toString();
} catch (final Exception e) {
s = e.getLocalizedMessage();
}
padf(s, _keyDisplayLength);
}
protected void valuef(final Value value) {
String s = null;
try {
if (value.getEncodedSize() >= Buffer.LONGREC_SIZE
&& (value.getEncodedBytes()[0] & 0xFF) == Buffer.LONGREC_TYPE) {
final long page = Buffer.decodeLongRecordDescriptorPointer(value.getEncodedBytes(), 0);
final int size = Buffer.decodeLongRecordDescriptorSize(value.getEncodedBytes(), 0);
s = String.format("LONG_REC size %,8d page %12d", size, page);
} else {
s = value.toString();
}
} catch (final Exception e) {
s = e.getLocalizedMessage();
}
padf(s, _valueDisplayLength);
}
protected void padf(final String s, final int length) {
if (s.length() < length) {
sb.append(s);
for (int i = length - s.length(); --i >= 0;) {
sb.append(' ');
}
} else {
sb.append(s.substring(0, length - ELIPSES.length()));
sb.append(ELIPSES);
}
}
protected void flush() {
if (sb.length() > 0) {
write(sb.toString());
sb.setLength(0);
}
}
protected void write(final String msg) {
_writer.println(msg);
}
// -----------------------
@Override
public void jh(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, recordSize);
final long baseAddress = JH.getBaseJournalAddress(_readBuffer);
final long blockSize = JH.getBlockSize(_readBuffer);
final String fileCreated = SDF.format(new Date(JH.getFileCreatedTime(_readBuffer)));
final String journalCreated = SDF.format(new Date(JH.getJournalCreatedTime(_readBuffer)));
final long version = JH.getVersion(_readBuffer);
start(address, timestamp, "JH", recordSize);
appendf(" version %,3d blockSize %,14d baseAddress %,18d journalCreated %24s fileCreated %24s", version,
blockSize, baseAddress, journalCreated, fileCreated);
_blockSize = blockSize;
flush();
}
@Override
public void je(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, recordSize);
start(address, timestamp, "JE", recordSize);
final long baseAddress = JE.getBaseAddress(_readBuffer);
final long currentAddress = JE.getCurrentJournalAddress(_readBuffer);
final String journalCreated = SDF.format(new Date(JE.getJournalCreatedTime(_readBuffer)));
appendf(" baseAddress %,18d currentAddress %,18d journalCreated %24s", baseAddress, currentAddress,
journalCreated);
flush();
}
@Override
public void iv(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, recordSize);
final int handle = IV.getHandle(_readBuffer);
final long id = IV.getVolumeId(_readBuffer);
final String name = IV.getVolumeSpecification(_readBuffer);
start(address, timestamp, "IV", recordSize);
appendf(" handle %05d id %,22d name %s", handle, id, name);
flush();
}
@Override
public void it(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, recordSize);
final int handle = IT.getHandle(_readBuffer);
final int vhandle = IT.getVolumeHandle(_readBuffer);
final String treeName = IT.getTreeName(_readBuffer);
start(address, timestamp, "IT", recordSize);
appendf(" handle %05d volume %05d treeName %s", handle, vhandle, treeName);
flush();
}
@Override
public void cp(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, recordSize);
final long baseAddress = CP.getBaseAddress(_readBuffer);
final String wallTime = SDF.format(new Date(CP.getSystemTimeMillis(_readBuffer)));
start(address, timestamp, "CP", recordSize);
appendf(" baseAddress %,18d at %24s", baseAddress, wallTime);
flush();
}
@Override
public void tx(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, recordSize);
start(address, timestamp, "TX", recordSize);
appendf(" committed %,d backchain %,d", TX.getCommitTimestamp(_readBuffer),
TX.getBackchainAddress(_readBuffer));
flush();
final int start = _readBuffer.position();
final int end = start + recordSize;
_readBuffer.position(_readBuffer.position() + TX.OVERHEAD);
while (_readBuffer.position() < end) {
final int innerSize = getLength(_readBuffer);
final int type = getType(_readBuffer);
final int position = _readBuffer.position();
processOneRecord(address + position - start, timestamp, innerSize, type);
_readBuffer.position(position + innerSize);
}
flush();
}
@Override
public void dr(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, recordSize);
final int thandle = DR.getTreeHandle(_readBuffer);
final int key1Size = DR.getKey1Size(_readBuffer);
final int elisionCount = DR.getKey2Elision(_readBuffer);
final int key2Size = recordSize - key1Size - DR.OVERHEAD;
System.arraycopy(_readBuffer.array(), _readBuffer.position() + DR.OVERHEAD, key1.getEncodedBytes(), 0,
key1Size);
key1.setEncodedSize(key1Size);
System.arraycopy(key1.getEncodedBytes(), 0, key2.getEncodedBytes(), 0, elisionCount);
System.arraycopy(_readBuffer.array(), _readBuffer.position() + DR.OVERHEAD + key1Size,
key2.getEncodedBytes(), elisionCount, key2Size);
key2.setEncodedSize(key2Size + elisionCount);
start(address, timestamp, "DR", recordSize);
appendf(" tree %05d key1Size %,5d key2Size %,5d ", thandle, key1Size, key2Size);
keyf(key1);
sb.append("->");
keyf(key2);
flush();
}
@Override
public void sr(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, recordSize);
final int thandle = SR.getTreeHandle(_readBuffer);
final int keySize = SR.getKeySize(_readBuffer);
final int valueSize = recordSize - keySize - SR.OVERHEAD;
System.arraycopy(_readBuffer.array(), _readBuffer.position() + SR.OVERHEAD, key1.getEncodedBytes(), 0,
keySize);
key1.setEncodedSize(keySize);
value.ensureFit(valueSize);
System.arraycopy(_readBuffer.array(), _readBuffer.position() + SR.OVERHEAD + keySize,
value.getEncodedBytes(), 0, valueSize);
value.setEncodedSize(valueSize);
start(address, timestamp, "SR", recordSize);
appendf(" tree %05d keySize %,5d valueSize %,5d ", thandle, keySize, valueSize);
keyf(key1);
sb.append(" : ");
valuef(value);
flush();
}
@Override
public void dt(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, DT.OVERHEAD);
final int thandle = DT.getTreeHandle(_readBuffer);
start(address, timestamp, "DT", recordSize);
appendf(" tree %05d", thandle);
flush();
}
@Override
public void pa(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, recordSize);
final long pageAddress = PA.getPageAddress(_readBuffer);
final int volumeHandle = PA.getVolumeHandle(_readBuffer);
if (!_selectedPages.isSelected(pageAddress)) {
return;
}
start(address, timestamp, "PA", recordSize);
final int type = JournalRecord.getByte(_readBuffer, PA.OVERHEAD + Buffer.TYPE_OFFSET);
final String typeString = Buffer.getPageTypeName(pageAddress, type);
final long rightSibling = pageAddress == 0 ? 0 : JournalRecord.getLong(_readBuffer, PA.OVERHEAD
+ Buffer.RIGHT_SIBLING_OFFSET);
appendf(" page %5d:%,12d type %10s right %,12d", volumeHandle, pageAddress, typeString, rightSibling);
flush();
}
@Override
public void pm(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, PM.OVERHEAD);
start(address, timestamp, "PM", recordSize);
final int count = PM.getEntryCount(_readBuffer);
appendf(" entries %,10d", count);
flush();
if (_verbose) {
dumpPageMap(count, address, timestamp, recordSize);
flush();
}
}
@Override
public void tm(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, TM.OVERHEAD);
start(address, timestamp, "TM", recordSize);
final int count = TM.getEntryCount(_readBuffer);
appendf(" entries %,10d", count);
flush();
if (_verbose) {
dumpTransactionMap(count, address, timestamp, recordSize);
flush();
}
}
@Override
public void d0(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, recordSize);
start(address, timestamp, "D0", recordSize);
final int thandle = D0.getTreeHandle(_readBuffer);
final int index = D0.getIndex(_readBuffer);
appendf(" tree %05d index %2d value %,5d", thandle, index, 1);
flush();
}
@Override
public void d1(final long address, final long timestamp, final int recordSize) throws Exception {
read(address, recordSize);
start(address, timestamp, "D1", recordSize);
final int thandle = D1.getTreeHandle(_readBuffer);
final int index = D1.getIndex(_readBuffer);
final long value = D1.getValue(_readBuffer);
appendf(" tree %05d index %2d value %,5d", thandle, index, value);
flush();
}
@Override
public void eof(final long address) throws Exception {
start(address, 0, "~~", 0);
flush();
}
void dumpPageMap(final int count, final long from, final long timestamp, final int recordSize)
throws PersistitIOException {
if (count * PM.ENTRY_SIZE + PM.OVERHEAD != recordSize) {
throw new CorruptJournalException("Invalid record size " + recordSize + " for PM record at "
+ addressToString(from, timestamp));
}
long address = from + PM.OVERHEAD;
int index = 0;
int loaded = 0;
long lastPage = Long.MAX_VALUE;
int lastVolumeHandle = Integer.MAX_VALUE;
for (int remaining = count; remaining > 0; remaining--) {
if (index == loaded) {
final int loadedSize = Math.min((_readBuffer.capacity() / PM.ENTRY_SIZE) * PM.ENTRY_SIZE, remaining
* PM.ENTRY_SIZE);
read(address, loadedSize);
address += loadedSize;
index = 0;
loaded = loadedSize / PM.ENTRY_SIZE;
if (loaded <= 0) {
throw new CorruptJournalException("Could not load PageMap segment in entry "
+ (count - remaining + 1) + " at " + addressToString(from, timestamp));
}
}
final int volumeHandle = PM.getEntryVolumeHandle(_readBuffer, index);
final long pageAddress = PM.getEntryPageAddress(_readBuffer, index);
final long pageTimestamp = PM.getEntryTimestamp(_readBuffer, index);
final long journalAddress = PM.getEntryJournalAddress(_readBuffer, index);
if (_selectedPages.isSelected(pageAddress) && _selectedTimestamps.isSelected(pageTimestamp)) {
if (pageAddress != lastPage || volumeHandle != lastVolumeHandle) {
flush();
lastPage = pageAddress;
lastVolumeHandle = volumeHandle;
appendf("-- %5d:%,12d: ", volumeHandle, pageAddress);
}
appendf(" @%,d(%,d)", journalAddress, pageTimestamp);
}
index++;
}
}
void dumpTransactionMap(final int count, final long from, final long timestamp, final int recordSize)
throws PersistitIOException {
if (count * TM.ENTRY_SIZE + TM.OVERHEAD != recordSize) {
throw new CorruptJournalException("Invalid record size " + recordSize + " for TM record at "
+ addressToString(from, timestamp));
}
long address = from + TM.OVERHEAD;
int index = 0;
int loaded = 0;
for (int remaining = count; remaining > 0; remaining--) {
if (index == loaded) {
read(address, Math.min(_readBuffer.capacity(), remaining * TM.ENTRY_SIZE));
address += _readBuffer.remaining();
index = 0;
loaded = _readBuffer.remaining() / TM.ENTRY_SIZE;
if (loaded <= 0) {
throw new CorruptJournalException("Could not load TransactionMap segment in entry "
+ (count - remaining + 1) + " at " + addressToString(from, timestamp));
}
}
final long startTimestamp = TM.getEntryStartTimestamp(_readBuffer, index);
final long commitTimestamp = TM.getEntryCommitTimestamp(_readBuffer, index);
final long journalAddress = TM.getEntryJournalAddress(_readBuffer, index);
final boolean isCommitted = commitTimestamp != 0;
appendf("-- start %,12d commit %,12d @%,18d %s", startTimestamp, commitTimestamp, journalAddress,
isCommitted ? "committed" : "uncommitted");
flush();
index++;
}
}
}
}