diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/PageCacheSlowTest.java b/community/io/src/test/java/org/neo4j/io/pagecache/PageCacheSlowTest.java
index 576c37e476e7d..f3c552f58d364 100644
--- a/community/io/src/test/java/org/neo4j/io/pagecache/PageCacheSlowTest.java
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/PageCacheSlowTest.java
@@ -40,9 +40,9 @@
import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.io.pagecache.tracing.PageCacheTracer;
import org.neo4j.io.pagecache.tracing.cursor.PageCursorTracerSupplier;
-import org.neo4j.test.LinearHistoryPageCacheTracer;
+import org.neo4j.io.pagecache.tracing.linear.LinearHistoryTracerFactory;
+import org.neo4j.io.pagecache.tracing.linear.LinearTracers;
import org.neo4j.test.rule.RepeatRule;
-import org.neo4j.test.LinearHistoryPageCursorTracer;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
@@ -454,9 +454,9 @@ public void pageCacheMustRemainInternallyConsistentWhenGettingRandomFailures() t
// Because our test failures are non-deterministic, we use this tracer to capture a full history of the
// events leading up to any given failure.
- LinearHistoryPageCacheTracer tracer = new LinearHistoryPageCacheTracer();
- //TODO:sdfasdf
- getPageCache( fs, maxPages, pageCachePageSize, tracer, LinearHistoryPageCursorTracer::new );
+ LinearTracers linearTracers = LinearHistoryTracerFactory.pageCacheTracer();
+ getPageCache( fs, maxPages, pageCachePageSize, linearTracers.getPageCacheTracer(),
+ linearTracers.getCursorTracerSupplier() );
PagedFile pfA = pageCache.map( existingFile( "a" ), filePageSize );
PagedFile pfB = pageCache.map( existingFile( "b" ), filePageSize / 2 + 1 );
@@ -525,7 +525,7 @@ public void pageCacheMustRemainInternallyConsistentWhenGettingRandomFailures() t
}
catch ( Throwable e )
{
- tracer.printHistory( System.err );
+ linearTracers.printHistory( System.err );
throw e;
}
}
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/randomharness/RandomPageCacheTestHarness.java b/community/io/src/test/java/org/neo4j/io/pagecache/randomharness/RandomPageCacheTestHarness.java
index 5b9bda02f8966..099a53446487d 100644
--- a/community/io/src/test/java/org/neo4j/io/pagecache/randomharness/RandomPageCacheTestHarness.java
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/randomharness/RandomPageCacheTestHarness.java
@@ -44,7 +44,10 @@
import org.neo4j.io.pagecache.impl.SingleFilePageSwapperFactory;
import org.neo4j.io.pagecache.impl.muninn.MuninnPageCache;
import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+import org.neo4j.io.pagecache.tracing.cursor.DefaultPageCursorTracerSupplier;
import org.neo4j.io.pagecache.tracing.cursor.PageCursorTracerSupplier;
+import org.neo4j.io.pagecache.tracing.linear.LinearHistoryPageCacheTracerTest;
+import org.neo4j.io.pagecache.tracing.linear.LinearTracers;
/**
* The RandomPageCacheTestHarness can plan and run random page cache tests, repeatably if necessary, and verify that
@@ -54,7 +57,7 @@
* before and after executing the planned test respectively, and it can integrate with the adversarial file system
* for fault injection, and arbitrary PageCacheTracers.
*
- * See {@link org.neo4j.test.LinearHistoryPageCacheTracerTest} for an example of how to configure and use the harness.
+ * See {@link LinearHistoryPageCacheTracerTest} for an example of how to configure and use the harness.
*/
public class RandomPageCacheTestHarness implements Closeable
{
@@ -92,7 +95,7 @@ public RandomPageCacheTestHarness()
filePageCount = cachePageCount * 10;
filePageSize = cachePageSize;
tracer = PageCacheTracer.NULL;
- cursorTracerSupplier = PageCursorTracerSupplier.NULL;
+ cursorTracerSupplier = DefaultPageCursorTracerSupplier.INSTANCE;
commandCount = 1000;
Command[] commands = Command.values();
@@ -150,6 +153,14 @@ public void setTracer( PageCacheTracer tracer )
this.tracer = tracer;
}
+ /**
+ * Set the page cursor tracers supplier.
+ */
+ public void setCursorTracerSupplier( PageCursorTracerSupplier cursorTracerSupplier )
+ {
+ this.cursorTracerSupplier = cursorTracerSupplier;
+ }
+
/**
* Set the mischief rate for the adversarial file system.
*/
@@ -227,7 +238,7 @@ public void setCommandCount( int commandCount )
/**
* Set the preparation phase to use. This phase is executed before all the planned commands. It can be used to
* prepare some file contents, or reset some external state, such as the
- * {@link org.neo4j.test.LinearHistoryPageCacheTracer}.
+ * {@link LinearTracers}.
*
* The preparation phase is executed before each iteration.
*/
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/tracing/DelegatingPageCursorTracer.java b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/DelegatingPageCursorTracer.java
deleted file mode 100644
index cdfa006b08510..0000000000000
--- a/community/io/src/test/java/org/neo4j/io/pagecache/tracing/DelegatingPageCursorTracer.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2002-2017 "Neo Technology,"
- * Network Engine for Objects in Lund AB [http://neotechnology.com]
- *
- * This file is part of Neo4j.
- *
- * Neo4j is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package org.neo4j.io.pagecache.tracing;
-
-import org.neo4j.io.pagecache.PageSwapper;
-import org.neo4j.io.pagecache.tracing.cursor.PageCursorTracer;
-
-public class DelegatingPageCursorTracer implements PageCursorTracer
-{
-
- private final PageCursorTracer delegate;
-
- public DelegatingPageCursorTracer( PageCursorTracer delegate )
- {
- this.delegate = delegate;
- }
-
- @Override
- public long faults()
- {
- return 0;
- }
-
- @Override
- public long pins()
- {
- return 0;
- }
-
- @Override
- public long unpins()
- {
- return 0;
- }
-
- @Override
- public long bytesRead()
- {
- return 0;
- }
-
- @Override
- public PinEvent beginPin( boolean writeLock, long filePageId, PageSwapper swapper )
- {
- return null;
- }
-
- @Override
- public void init( PageCacheTracer tracer )
- {
-
- }
-
- @Override
- public void reportEvents()
- {
-
- }
-}
diff --git a/community/io/src/test/java/org/neo4j/test/LinearHistoryPageCacheTracer.java b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/HEvents.java
similarity index 53%
rename from community/io/src/test/java/org/neo4j/test/LinearHistoryPageCacheTracer.java
rename to community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/HEvents.java
index 321ec3ed3196f..76a6a7912e1d0 100644
--- a/community/io/src/test/java/org/neo4j/test/LinearHistoryPageCacheTracer.java
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/HEvents.java
@@ -17,22 +17,16 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.neo4j.test;
+package org.neo4j.io.pagecache.tracing.linear;
-import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
-import java.io.OutputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.util.IdentityHashMap;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Consumer;
import org.neo4j.io.pagecache.PageSwapper;
import org.neo4j.io.pagecache.tracing.EvictionEvent;
@@ -40,132 +34,36 @@
import org.neo4j.io.pagecache.tracing.FlushEvent;
import org.neo4j.io.pagecache.tracing.FlushEventOpportunity;
import org.neo4j.io.pagecache.tracing.MajorFlushEvent;
-import org.neo4j.io.pagecache.tracing.PageCacheTracer;
import org.neo4j.io.pagecache.tracing.PageFaultEvent;
import org.neo4j.io.pagecache.tracing.PinEvent;
/**
- * This PageCacheTracer records a linearized history of the internal page cache events.
- *
- * This takes up a lot of heap memory, because nothing is ever thrown away.
- *
- * Only use this for debugging internal data race bugs and the like, in the page cache.
+ * Container of events for page cache tracers that are used to build linear historical representation of page cache
+ * events.
+ * In case if event can generate any other event it will properly add it to corresponding tracer and it will be also
+ * tracked.
+ * @see LinearHistoryTracer
*/
-public final class LinearHistoryPageCacheTracer implements PageCacheTracer
+class HEvents
{
- private final AtomicReference history = new AtomicReference<>();
-
- // The output buffering mechanics are pre-allocated in case we have to deal with low-memory situations.
- // The output switching is guarded by the monitor lock on the LinearHistoryPageCacheTracer instance.
- // The class name cache is similarly guarded the monitor lock. In short, only a single thread can print history
- // at a time.
- private final SwitchableBufferedOutputStream bufferOut = new SwitchableBufferedOutputStream();
- private final PrintStream out = new PrintStream( bufferOut );
- private final Map, String> classSimpleNameCache = new IdentityHashMap<>();
-
- private static class SwitchableBufferedOutputStream extends BufferedOutputStream
+ private HEvents()
{
-
- public SwitchableBufferedOutputStream()
- {
- //noinspection ConstantConditions
- super( null ); // No output target by default. This is changed in printHistory.
- }
-
- public void setOut( OutputStream out )
- {
- super.out = out;
- }
}
- private final HEvent end = new HEvent()
- {
- @Override
- void printBody( PrintStream out, String exceptionLinePrefix )
- {
- out.print( " EOF " );
- }
- };
-
- public abstract class HEvent
+ static final class EndHEvent extends HEvent
{
- final long time;
- final long threadId;
- final String threadName;
- volatile HEvent prev;
-
- private HEvent()
- {
- time = System.nanoTime();
- Thread thread = Thread.currentThread();
- threadId = thread.getId();
- threadName = thread.getName();
- System.identityHashCode( this );
- }
-
- public final void print( PrintStream out, String exceptionLinePrefix )
- {
- if ( getClass() == EndHEvent.class )
- {
- out.print( '-' );
- }
- out.print( getClass().getSimpleName() );
- out.print( '#' );
- out.print( System.identityHashCode( this ) );
- out.print( '[' );
- out.print( "time:" );
- out.print( (time - end.time) / 1000 );
- out.print( ", threadId:" );
- out.print( threadId );
- printBody( out, exceptionLinePrefix );
- out.print( ']' );
- }
-
- abstract void printBody( PrintStream out, String exceptionLinePrefix );
+ private static final Map,String> classSimpleNameCache = new IdentityHashMap<>();
+ IntervalHEvent event;
- protected final void print( PrintStream out, File file )
+ EndHEvent( IntervalHEvent event )
{
- out.print( ", file:" );
- out.print( file == null ? "" : file.getPath() );
- }
-
- protected final void print( PrintStream out, Throwable exception, String linePrefix )
- {
- if ( exception != null )
- {
- out.println( ", exception:" );
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
- PrintStream sbuf = new PrintStream( buf );
- exception.printStackTrace( sbuf );
- sbuf.flush();
- BufferedReader reader = new BufferedReader( new StringReader( buf.toString() ) );
- try
- {
- String line = reader.readLine();
- while ( line != null )
- {
- out.print( linePrefix );
- out.print( '\t' );
- out.println( line );
- line = reader.readLine();
- }
- out.print( linePrefix );
- }
- catch ( IOException e )
- {
- throw new RuntimeException( e );
- }
- }
+ this.event = event;
}
- }
-
- public final class EndHEvent extends HEvent
- {
- IntervalHEven event;
- public EndHEvent( IntervalHEven event )
+ public void print( PrintStream out, String exceptionLinePrefix )
{
- this.event = event;
+ out.print( '-' );
+ super.print( out, exceptionLinePrefix );
}
@Override
@@ -174,30 +72,22 @@ void printBody( PrintStream out, String exceptionLinePrefix )
out.print( ", elapsedMicros:" );
out.print( (time - event.time) / 1000 );
out.print( ", endOf:" );
- Class extends IntervalHEven> eventClass = event.getClass();
- String className = classSimpleNameCache.get( eventClass );
- if ( className == null )
- {
- className = eventClass.getSimpleName();
- classSimpleNameCache.put( eventClass, className );
- }
+ Class extends IntervalHEvent> eventClass = event.getClass();
+ String className = classSimpleNameCache.computeIfAbsent( eventClass, k -> eventClass.getSimpleName() );
out.print( className );
out.print( '#' );
out.print( System.identityHashCode( event ) );
}
}
- public abstract class IntervalHEven extends HEvent
+ static class MappedFileHEvent extends HEvent
{
- public void close()
+ File file;
+
+ MappedFileHEvent( File file )
{
- add( new EndHEvent( this ) );
+ this.file = file;
}
- }
-
- public class MappedFileHEvent extends HEvent
- {
- File file;
@Override
void printBody( PrintStream out, String exceptionLinePrefix )
@@ -206,10 +96,15 @@ void printBody( PrintStream out, String exceptionLinePrefix )
}
}
- public class UnmappedFileHEvent extends HEvent
+ static class UnmappedFileHEvent extends HEvent
{
File file;
+ UnmappedFileHEvent( File file )
+ {
+ this.file = file;
+ }
+
@Override
void printBody( PrintStream out, String exceptionLinePrefix )
{
@@ -217,19 +112,20 @@ void printBody( PrintStream out, String exceptionLinePrefix )
}
}
- public class EvictionRunHEvent extends IntervalHEven implements EvictionRunEvent
+ public static class EvictionRunHEvent extends IntervalHEvent implements EvictionRunEvent
{
int pagesToEvict;
- private EvictionRunHEvent( int pagesToEvict )
+ EvictionRunHEvent( LinearHistoryTracer tracer, int pagesToEvict )
{
+ super( tracer );
this.pagesToEvict = pagesToEvict;
}
@Override
public EvictionEvent beginEviction()
{
- return add( new EvictionHEvent() );
+ return tracer.add( new EvictionHEvent( tracer ) );
}
@Override
@@ -240,47 +136,47 @@ void printBody( PrintStream out, String exceptionLinePrefix )
}
}
- public class EvictionHEvent extends IntervalHEven implements EvictionEvent, FlushEventOpportunity
+ public static class FlushHEvent extends IntervalHEvent implements FlushEvent
{
private long filePageId;
+ private int cachePageId;
+ private int pageCount;
private File file;
+ private int bytesWritten;
private IOException exception;
- private int cachePageId;
- @Override
- public void setFilePageId( long filePageId )
+ FlushHEvent( LinearHistoryTracer tracer, long filePageId, int cachePageId, PageSwapper swapper )
{
+ super( tracer );
this.filePageId = filePageId;
+ this.cachePageId = cachePageId;
+ this.pageCount = 1;
+ this.file = swapper.file();
}
@Override
- public void setSwapper( PageSwapper swapper )
+ public void addBytesWritten( long bytes )
{
- file = swapper == null? null : swapper.file();
+ bytesWritten += bytes;
}
@Override
- public FlushEventOpportunity flushEventOpportunity()
+ public void done()
{
- return this;
+ close();
}
@Override
- public void threwException( IOException exception )
+ public void done( IOException exception )
{
this.exception = exception;
+ done();
}
@Override
- public void setCachePageId( int cachePageId )
- {
- this.cachePageId = cachePageId;
- }
-
- @Override
- public FlushEvent beginFlush( long filePageId, int cachePageId, PageSwapper swapper )
+ public void addPagesFlushed( int pageCount )
{
- return add( new FlushHEvent( filePageId, cachePageId, swapper ) );
+ this.pageCount = pageCount;
}
@Override
@@ -290,78 +186,54 @@ void printBody( PrintStream out, String exceptionLinePrefix )
out.print( filePageId );
out.print( ", cachePageId:" );
out.print( cachePageId );
+ out.print( ", pageCount:" );
+ out.print( pageCount );
print( out, file );
+ out.print( ", bytesWritten:" );
+ out.print( bytesWritten );
print( out, exception, exceptionLinePrefix );
}
}
- public class FlushHEvent extends IntervalHEven implements FlushEvent
+ public static class MajorFlushHEvent extends IntervalHEvent implements MajorFlushEvent, FlushEventOpportunity
{
- private long filePageId;
- private int cachePageId;
- private int pageCount;
private File file;
- private int bytesWritten;
- private IOException exception;
-
- public FlushHEvent( long filePageId, int cachePageId, PageSwapper swapper )
- {
- this.filePageId = filePageId;
- this.cachePageId = cachePageId;
- this.pageCount = 1;
- this.file = swapper.file();
- }
-
- @Override
- public void addBytesWritten( long bytes )
- {
- bytesWritten += bytes;
- }
- @Override
- public void done()
+ MajorFlushHEvent( LinearHistoryTracer tracer, File file )
{
- close();
+ super( tracer );
+ this.file = file;
}
@Override
- public void done( IOException exception )
+ public FlushEventOpportunity flushEventOpportunity()
{
- this.exception = exception;
- done();
+ return this;
}
@Override
- public void addPagesFlushed( int pageCount )
+ public FlushEvent beginFlush( long filePageId, int cachePageId, PageSwapper swapper )
{
- this.pageCount = pageCount;
+ return tracer.add( new FlushHEvent( tracer, filePageId, cachePageId, swapper ) );
}
@Override
void printBody( PrintStream out, String exceptionLinePrefix )
{
- out.print( ", filePageId:" );
- out.print( filePageId );
- out.print( ", cachePageId:" );
- out.print( cachePageId );
- out.print( ", pageCount:" );
- out.print( pageCount );
print( out, file );
- out.print( ", bytesWritten:" );
- out.print( bytesWritten );
- print( out, exception, exceptionLinePrefix );
}
}
- public class PinHEvent extends IntervalHEven implements PinEvent
+ public static class PinHEvent extends IntervalHEvent implements PinEvent
{
private boolean exclusiveLock;
private long filePageId;
private File file;
private int cachePageId;
- public PinHEvent( boolean exclusiveLock, long filePageId, PageSwapper swapper )
+ PinHEvent( LinearHistoryTracer tracer, boolean exclusiveLock, long filePageId, PageSwapper swapper )
{
+ super( tracer );
this.exclusiveLock = exclusiveLock;
this.filePageId = filePageId;
this.file = swapper.file();
@@ -376,7 +248,7 @@ public void setCachePageId( int cachePageId )
@Override
public PageFaultEvent beginPageFault()
{
- return add( new PageFaultHEvent() );
+ return tracer.add( new PageFaultHEvent( tracer ) );
}
@Override
@@ -398,13 +270,18 @@ void printBody( PrintStream out, String exceptionLinePrefix )
}
}
- public class PageFaultHEvent extends IntervalHEven implements PageFaultEvent
+ public static class PageFaultHEvent extends IntervalHEvent implements PageFaultEvent
{
private int bytesRead;
private int cachePageId;
private boolean pageEvictedByFaulter;
private Throwable exception;
+ PageFaultHEvent( LinearHistoryTracer linearHistoryTracer )
+ {
+ super( linearHistoryTracer );
+ }
+
@Override
public void addBytesRead( long bytes )
{
@@ -434,7 +311,7 @@ public void done( Throwable throwable )
public EvictionEvent beginEviction()
{
pageEvictedByFaulter = true;
- return add( new EvictionHEvent() );
+ return tracer.add( new EvictionHEvent( tracer ) );
}
@Override
@@ -450,279 +327,173 @@ void printBody( PrintStream out, String exceptionLinePrefix )
}
}
- public class MajorFlushHEvent extends IntervalHEven implements MajorFlushEvent, FlushEventOpportunity
+ public static class EvictionHEvent extends IntervalHEvent implements EvictionEvent, FlushEventOpportunity
{
+ private long filePageId;
private File file;
+ private IOException exception;
+ private int cachePageId;
- public MajorFlushHEvent( File file )
+ EvictionHEvent( LinearHistoryTracer linearHistoryTracer )
{
- this.file = file;
+ super( linearHistoryTracer );
}
@Override
- public FlushEventOpportunity flushEventOpportunity()
+ public void setFilePageId( long filePageId )
{
- return this;
+ this.filePageId = filePageId;
}
@Override
- public FlushEvent beginFlush( long filePageId, int cachePageId, PageSwapper swapper )
+ public void setSwapper( PageSwapper swapper )
{
- return add( new FlushHEvent( filePageId, cachePageId, swapper ) );
+ file = swapper == null ? null : swapper.file();
}
@Override
- void printBody( PrintStream out, String exceptionLinePrefix )
+ public FlushEventOpportunity flushEventOpportunity()
{
- print( out, file );
+ return this;
}
- }
-
- E add( E event )
- {
- HEvent prev = history.getAndSet( event );
- event.prev = prev == null? end : prev;
- return event;
- }
- public synchronized boolean processHistory( Consumer processor )
- {
- HEvent events = history.getAndSet( null );
- if ( events == null )
+ @Override
+ public void threwException( IOException exception )
{
- return false;
+ this.exception = exception;
}
- events = reverse( events );
- while ( events != null )
+
+ @Override
+ public void setCachePageId( int cachePageId )
{
- processor.accept( events );
- events = events.prev;
+ this.cachePageId = cachePageId;
}
- return true;
- }
- public synchronized void printHistory( PrintStream outputStream )
- {
- bufferOut.setOut( outputStream );
- if ( !processHistory( new HistoryPrinter() ) )
+ @Override
+ public FlushEvent beginFlush( long filePageId, int cachePageId, PageSwapper swapper )
{
- out.println( "No events recorded." );
+ return tracer.add( new FlushHEvent( tracer, filePageId, cachePageId, swapper ) );
}
- out.flush();
- }
- private HEvent reverse( HEvent events )
- {
- HEvent current = end;
- while ( events != end )
+ @Override
+ void printBody( PrintStream out, String exceptionLinePrefix )
{
- HEvent prev;
- do
- {
- prev = events.prev;
- } while ( prev == null );
- events.prev = current;
- current = events;
- events = prev;
+ out.print( ", filePageId:" );
+ out.print( filePageId );
+ out.print( ", cachePageId:" );
+ out.print( cachePageId );
+ print( out, file );
+ print( out, exception, exceptionLinePrefix );
}
- return current;
- }
-
- @Override
- public void mappedFile( File file )
- {
- add( new MappedFileHEvent() ).file = file;
- }
-
- @Override
- public void unmappedFile( File file )
- {
- add( new UnmappedFileHEvent() ).file = file;
- }
-
- @Override
- public EvictionRunEvent beginPageEvictions( int pageCountToEvict )
- {
- return add( new EvictionRunHEvent( pageCountToEvict ) );
- }
-
- @Override
- public MajorFlushEvent beginFileFlush( PageSwapper swapper )
- {
- return add( new MajorFlushHEvent( swapper.file() ) );
- }
-
- @Override
- public MajorFlushEvent beginCacheFlush()
- {
- return add( new MajorFlushHEvent( null ) );
- }
-
- @Override
- public long faults()
- {
- return 0;
- }
-
- @Override
- public long evictions()
- {
- return 0;
- }
-
- @Override
- public long pins()
- {
- return 0;
- }
-
- @Override
- public long unpins()
- {
- return 0;
- }
-
- @Override
- public long flushes()
- {
- return 0;
- }
-
- @Override
- public long bytesRead()
- {
- return 0;
- }
-
- @Override
- public long bytesWritten()
- {
- return 0;
- }
-
- @Override
- public long filesMapped()
- {
- return 0;
- }
-
- @Override
- public long filesUnmapped()
- {
- return 0;
- }
-
- @Override
- public long evictionExceptions()
- {
- return 0;
- }
-
- @Override
- public void pins( long pins )
- {
- }
-
- @Override
- public void unpins( long unpins )
- {
-
- }
-
- @Override
- public void faults( long faults )
- {
-
}
- @Override
- public void bytesRead( long bytesRead )
+ public abstract static class HEvent
{
+ static final HEvent end = new HEvent()
+ {
+ @Override
+ void printBody( PrintStream out, String exceptionLinePrefix )
+ {
+ out.print( " EOF " );
+ }
+ };
- }
-
- @Override
- public void evictions( long evictions )
- {
-
- }
-
- @Override
- public void bytesWritten( long bytesWritten )
- {
+ final long time;
+ final long threadId;
+ final String threadName;
+ volatile HEvent prev;
- }
+ HEvent()
+ {
+ time = System.nanoTime();
+ Thread thread = Thread.currentThread();
+ threadId = thread.getId();
+ threadName = thread.getName();
+ System.identityHashCode( this );
+ }
- @Override
- public void flushes( long flushes )
- {
+ public static HEvent reverse( HEvent events )
+ {
+ HEvent current = end;
+ while ( events != end )
+ {
+ HEvent prev;
+ do
+ {
+ prev = events.prev;
+ }
+ while ( prev == null );
+ events.prev = current;
+ current = events;
+ events = prev;
+ }
+ return current;
+ }
- }
+ public void print( PrintStream out, String exceptionLinePrefix )
+ {
+ out.print( getClass().getSimpleName() );
+ out.print( '#' );
+ out.print( System.identityHashCode( this ) );
+ out.print( '[' );
+ out.print( "time:" );
+ out.print( (time - end.time) / 1000 );
+ out.print( ", threadId:" );
+ out.print( threadId );
+ printBody( out, exceptionLinePrefix );
+ out.print( ']' );
+ }
- private class HistoryPrinter implements Consumer
- {
- private final List concurrentIntervals;
+ abstract void printBody( PrintStream out, String exceptionLinePrefix );
- public HistoryPrinter()
+ protected final void print( PrintStream out, File file )
{
- this.concurrentIntervals = new LinkedList<>();
+ out.print( ", file:" );
+ out.print( file == null ? "" : file.getPath() );
}
- @Override
- public void accept( HEvent event )
+ protected final void print( PrintStream out, Throwable exception, String linePrefix )
{
- String exceptionLinePrefix = exceptionLinePrefix( concurrentIntervals.size() );
- if ( event.getClass() == EndHEvent.class )
+ if ( exception != null )
{
- EndHEvent endHEvent = (EndHEvent) event;
- int idx = concurrentIntervals.indexOf( endHEvent.event );
- putcs( out, '|', idx );
- out.print( '-' );
- int left = concurrentIntervals.size() - idx - 1;
- putcs( out, '|', left );
- out.print( " " );
- endHEvent.print( out, exceptionLinePrefix );
- concurrentIntervals.remove( idx );
- if ( left > 0 )
+ out.println( ", exception:" );
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ PrintStream sbuf = new PrintStream( buf );
+ exception.printStackTrace( sbuf );
+ sbuf.flush();
+ BufferedReader reader = new BufferedReader( new StringReader( buf.toString() ) );
+ try
{
- out.println();
- putcs( out, '|', idx );
- putcs( out, '/', left );
+ String line = reader.readLine();
+ while ( line != null )
+ {
+ out.print( linePrefix );
+ out.print( '\t' );
+ out.println( line );
+ line = reader.readLine();
+ }
+ out.print( linePrefix );
+ }
+ catch ( IOException e )
+ {
+ throw new RuntimeException( e );
}
}
- else if ( event instanceof IntervalHEven )
- {
- putcs( out, '|', concurrentIntervals.size() );
- out.print( "+ " );
- event.print( out, exceptionLinePrefix );
- concurrentIntervals.add( event );
- }
- else
- {
- putcs( out, '|', concurrentIntervals.size() );
- out.print( "> " );
- event.print( out, exceptionLinePrefix );
- }
- out.println();
}
+ }
+
+ public abstract static class IntervalHEvent extends HEvent
+ {
+ protected LinearHistoryTracer tracer;
- private String exceptionLinePrefix( int size )
+ IntervalHEvent( LinearHistoryTracer tracer )
{
- StringBuilder sb = new StringBuilder();
- for ( int i = 0; i < size; i++ )
- {
- sb.append( '|' );
- }
- sb.append( ": " );
- return sb.toString();
+ this.tracer = tracer;
}
- private void putcs( PrintStream out, char c, int count )
+ public void close()
{
- for ( int i = 0; i < count; i++ )
- {
- out.print( c );
- }
+ tracer.add( new EndHEvent( this ) );
}
}
}
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryPageCacheTracer.java b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryPageCacheTracer.java
new file mode 100644
index 0000000000000..a217cd45e505d
--- /dev/null
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryPageCacheTracer.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2002-2017 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.io.pagecache.tracing.linear;
+
+import java.io.File;
+
+import org.neo4j.io.pagecache.PageSwapper;
+import org.neo4j.io.pagecache.tracing.EvictionRunEvent;
+import org.neo4j.io.pagecache.tracing.MajorFlushEvent;
+import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+
+import static org.neo4j.io.pagecache.tracing.linear.HEvents.EvictionRunHEvent;
+import static org.neo4j.io.pagecache.tracing.linear.HEvents.MajorFlushHEvent;
+import static org.neo4j.io.pagecache.tracing.linear.HEvents.MappedFileHEvent;
+import static org.neo4j.io.pagecache.tracing.linear.HEvents.UnmappedFileHEvent;
+
+/**
+ * Tracer for global page cache events that add all of them to event history tracer that can build proper linear
+ * history across all tracers.
+ * Only use this for debugging internal data race bugs and the like, in the page cache.
+ * @see HEvents
+ * @see LinearHistoryPageCursorTracer
+ */
+public final class LinearHistoryPageCacheTracer implements PageCacheTracer
+{
+
+ private LinearHistoryTracer tracer;
+
+ LinearHistoryPageCacheTracer( LinearHistoryTracer tracer )
+ {
+ this.tracer = tracer;
+ }
+
+ @Override
+ public void mappedFile( File file )
+ {
+ tracer.add( new MappedFileHEvent( file ) );
+ }
+
+ @Override
+ public void unmappedFile( File file )
+ {
+ tracer.add( new UnmappedFileHEvent( file ) );
+ }
+
+ @Override
+ public EvictionRunEvent beginPageEvictions( int pageCountToEvict )
+ {
+ return tracer.add( new EvictionRunHEvent( tracer, pageCountToEvict ) );
+ }
+
+ @Override
+ public MajorFlushEvent beginFileFlush( PageSwapper swapper )
+ {
+ return tracer.add( new MajorFlushHEvent( tracer, swapper.file() ) );
+ }
+
+ @Override
+ public MajorFlushEvent beginCacheFlush()
+ {
+ return tracer.add( new MajorFlushHEvent( tracer, null ) );
+ }
+
+ @Override
+ public long faults()
+ {
+ return 0;
+ }
+
+ @Override
+ public long evictions()
+ {
+ return 0;
+ }
+
+ @Override
+ public long pins()
+ {
+ return 0;
+ }
+
+ @Override
+ public long unpins()
+ {
+ return 0;
+ }
+
+ @Override
+ public long flushes()
+ {
+ return 0;
+ }
+
+ @Override
+ public long bytesRead()
+ {
+ return 0;
+ }
+
+ @Override
+ public long bytesWritten()
+ {
+ return 0;
+ }
+
+ @Override
+ public long filesMapped()
+ {
+ return 0;
+ }
+
+ @Override
+ public long filesUnmapped()
+ {
+ return 0;
+ }
+
+ @Override
+ public long evictionExceptions()
+ {
+ return 0;
+ }
+
+ @Override
+ public void pins( long pins )
+ {
+ }
+
+ @Override
+ public void unpins( long unpins )
+ {
+ }
+
+ @Override
+ public void faults( long faults )
+ {
+ }
+
+ @Override
+ public void bytesRead( long bytesRead )
+ {
+ }
+
+ @Override
+ public void evictions( long evictions )
+ {
+ }
+
+ @Override
+ public void bytesWritten( long bytesWritten )
+ {
+ }
+
+ @Override
+ public void flushes( long flushes )
+ {
+ }
+}
diff --git a/community/io/src/test/java/org/neo4j/test/LinearHistoryPageCacheTracerTest.java b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryPageCacheTracerTest.java
similarity index 82%
rename from community/io/src/test/java/org/neo4j/test/LinearHistoryPageCacheTracerTest.java
rename to community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryPageCacheTracerTest.java
index 0b79a1e38f173..93066edadcbbf 100644
--- a/community/io/src/test/java/org/neo4j/test/LinearHistoryPageCacheTracerTest.java
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryPageCacheTracerTest.java
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.neo4j.test;
+package org.neo4j.io.pagecache.tracing.linear;
import org.junit.Ignore;
import org.junit.Test;
@@ -34,18 +34,19 @@ public class LinearHistoryPageCacheTracerTest
@Test
public void makeSomeTestOutput() throws Exception
{
- final LinearHistoryPageCacheTracer tracer = new LinearHistoryPageCacheTracer();
+ LinearTracers linearTracers = LinearHistoryTracerFactory.pageCacheTracer();
try ( RandomPageCacheTestHarness harness = new RandomPageCacheTestHarness() )
{
harness.setUseAdversarialIO( true );
- harness.setTracer( tracer );
+ harness.setTracer( linearTracers.getPageCacheTracer() );
+ harness.setCursorTracerSupplier( linearTracers.getCursorTracerSupplier() );
harness.setCommandCount( 100 );
harness.setConcurrencyLevel( 2 );
- harness.setPreparation( ( pageCache, fs, files ) -> tracer.processHistory( hEvent -> {} ) );
+ harness.setPreparation( ( pageCache, fs, files ) -> linearTracers.processHistory( hEvent -> {} ) );
harness.run( 1, TimeUnit.MINUTES );
- tracer.printHistory( System.out );
+ linearTracers.printHistory( System.out );
}
}
diff --git a/community/io/src/test/java/org/neo4j/test/LinearHistoryPageCursorTracer.java b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryPageCursorTracer.java
similarity index 71%
rename from community/io/src/test/java/org/neo4j/test/LinearHistoryPageCursorTracer.java
rename to community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryPageCursorTracer.java
index 204c657c057ce..82c154176d071 100644
--- a/community/io/src/test/java/org/neo4j/test/LinearHistoryPageCursorTracer.java
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryPageCursorTracer.java
@@ -17,15 +17,29 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.neo4j.test;
+package org.neo4j.io.pagecache.tracing.linear;
import org.neo4j.io.pagecache.PageSwapper;
import org.neo4j.io.pagecache.tracing.PageCacheTracer;
import org.neo4j.io.pagecache.tracing.PinEvent;
import org.neo4j.io.pagecache.tracing.cursor.PageCursorTracer;
+/**
+ * Tracer for page cache cursor events that add all of them to event history tracer that can build proper linear
+ * history across all tracers.
+ * Only use this for debugging internal data race bugs and the like, in the page cache.
+ *
+ * @see HEvents
+ * @see LinearHistoryPageCacheTracer
+ */
public class LinearHistoryPageCursorTracer implements PageCursorTracer
{
+ private LinearHistoryTracer tracer;
+
+ LinearHistoryPageCursorTracer( LinearHistoryTracer tracer )
+ {
+ this.tracer = tracer;
+ }
@Override
public long faults()
@@ -51,23 +65,21 @@ public long bytesRead()
return 0;
}
- //TODO:
@Override
public PinEvent beginPin( boolean writeLock, long filePageId, PageSwapper swapper )
{
- return PinEvent.NULL;
+ return tracer.add( new HEvents.PinHEvent( tracer, writeLock, filePageId, swapper ) );
}
@Override
public void init( PageCacheTracer tracer )
{
-
+ // nothing to do
}
@Override
public void reportEvents()
{
-
+ // nothing to do
}
-
}
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryTracer.java b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryTracer.java
new file mode 100644
index 0000000000000..8085aaf6258bc
--- /dev/null
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryTracer.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2002-2017 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.io.pagecache.tracing.linear;
+
+import java.io.BufferedOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+/**
+ * Records a linearized history of all (global and cursors) internal page cache events.
+ * Only use this for debugging internal data race bugs and the like, in the page cache.
+ */
+class LinearHistoryTracer
+{
+ private final AtomicReference history = new AtomicReference<>();
+
+ // The output buffering mechanics are pre-allocated in case we have to deal with low-memory situations.
+ // The output switching is guarded by the monitor lock on the LinearHistoryPageCacheTracer instance.
+ // The class name cache is similarly guarded the monitor lock. In short, only a single thread can print history
+ // at a time.
+ private final SwitchableBufferedOutputStream bufferOut = new SwitchableBufferedOutputStream();
+ private final PrintStream out = new PrintStream( bufferOut );
+
+ synchronized boolean processHistory( Consumer processor )
+ {
+ HEvents.HEvent events = history.getAndSet( null );
+ if ( events == null )
+ {
+ return false;
+ }
+ events = HEvents.HEvent.reverse( events );
+ while ( events != null )
+ {
+ processor.accept( events );
+ events = events.prev;
+ }
+ return true;
+ }
+
+ E add( E event )
+ {
+ HEvents.HEvent prev = history.getAndSet( event );
+ event.prev = prev == null ? HEvents.HEvent.end : prev;
+ return event;
+ }
+
+ synchronized void printHistory( PrintStream outputStream )
+ {
+ bufferOut.setOut( outputStream );
+ if ( !processHistory( new HistoryPrinter() ) )
+ {
+ out.println( "No events recorded." );
+ }
+ out.flush();
+ }
+
+ private static class SwitchableBufferedOutputStream extends BufferedOutputStream
+ {
+
+ SwitchableBufferedOutputStream()
+ {
+ //noinspection ConstantConditions
+ super( null ); // No output target by default. This is changed in printHistory.
+ }
+
+ public void setOut( OutputStream out )
+ {
+ super.out = out;
+ }
+ }
+
+ private class HistoryPrinter implements Consumer
+ {
+ private final List concurrentIntervals;
+
+ HistoryPrinter()
+ {
+ this.concurrentIntervals = new LinkedList<>();
+ }
+
+ @Override
+ public void accept( HEvents.HEvent event )
+ {
+ String exceptionLinePrefix = exceptionLinePrefix( concurrentIntervals.size() );
+ if ( event.getClass() == HEvents.EndHEvent.class )
+ {
+ HEvents.EndHEvent endHEvent = (HEvents.EndHEvent) event;
+ int idx = concurrentIntervals.indexOf( endHEvent.event );
+ putcs( out, '|', idx );
+ out.print( '-' );
+ int left = concurrentIntervals.size() - idx - 1;
+ putcs( out, '|', left );
+ out.print( " " );
+ endHEvent.print( out, exceptionLinePrefix );
+ concurrentIntervals.remove( idx );
+ if ( left > 0 )
+ {
+ out.println();
+ putcs( out, '|', idx );
+ putcs( out, '/', left );
+ }
+ }
+ else if ( event instanceof HEvents.IntervalHEvent )
+ {
+ putcs( out, '|', concurrentIntervals.size() );
+ out.print( "+ " );
+ event.print( out, exceptionLinePrefix );
+ concurrentIntervals.add( event );
+ }
+ else
+ {
+ putcs( out, '|', concurrentIntervals.size() );
+ out.print( "> " );
+ event.print( out, exceptionLinePrefix );
+ }
+ out.println();
+ }
+
+ private String exceptionLinePrefix( int size )
+ {
+ StringBuilder sb = new StringBuilder();
+ for ( int i = 0; i < size; i++ )
+ {
+ sb.append( '|' );
+ }
+ sb.append( ": " );
+ return sb.toString();
+ }
+
+ private void putcs( PrintStream out, char c, int count )
+ {
+ for ( int i = 0; i < count; i++ )
+ {
+ out.print( c );
+ }
+ }
+ }
+}
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryTracerFactory.java b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryTracerFactory.java
new file mode 100644
index 0000000000000..aeaf45d0b9448
--- /dev/null
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearHistoryTracerFactory.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2002-2017 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.io.pagecache.tracing.linear;
+
+public class LinearHistoryTracerFactory
+{
+ public static LinearTracers pageCacheTracer()
+ {
+ LinearHistoryTracer tracer = new LinearHistoryTracer();
+ return new LinearTracers( new LinearHistoryPageCacheTracer( tracer ),
+ () -> new LinearHistoryPageCursorTracer( tracer ), tracer );
+ }
+}
diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearTracers.java b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearTracers.java
new file mode 100644
index 0000000000000..90206101e0f81
--- /dev/null
+++ b/community/io/src/test/java/org/neo4j/io/pagecache/tracing/linear/LinearTracers.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2002-2017 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.io.pagecache.tracing.linear;
+
+import java.io.PrintStream;
+import java.util.function.Consumer;
+
+import org.neo4j.io.pagecache.tracing.cursor.PageCursorTracerSupplier;
+
+public class LinearTracers
+{
+ private final LinearHistoryPageCacheTracer pageCacheTracer;
+ private final PageCursorTracerSupplier cursorTracerSupplier;
+ private final LinearHistoryTracer tracer;
+
+ LinearTracers( LinearHistoryPageCacheTracer pageCacheTracer, PageCursorTracerSupplier cursorTracerSupplier,
+ LinearHistoryTracer tracer )
+ {
+ this.pageCacheTracer = pageCacheTracer;
+ this.cursorTracerSupplier = cursorTracerSupplier;
+ this.tracer = tracer;
+ }
+
+ public LinearHistoryPageCacheTracer getPageCacheTracer()
+ {
+ return pageCacheTracer;
+ }
+
+ public PageCursorTracerSupplier getCursorTracerSupplier()
+ {
+ return cursorTracerSupplier;
+ }
+
+ public void printHistory( PrintStream err )
+ {
+ tracer.printHistory( err );
+ }
+
+ void processHistory( Consumer processor )
+ {
+ tracer.processHistory( processor );
+ }
+}