diff --git a/kieker-trace-diagnosis-application/pom.xml b/kieker-trace-diagnosis-application/pom.xml index 86619b3049ab4a23939cc26127ec56d1e4ebf087..72ceead4646b232c623a2335a58dddc54d5c015a 100644 --- a/kieker-trace-diagnosis-application/pom.xml +++ b/kieker-trace-diagnosis-application/pom.xml @@ -1,4 +1,5 @@ -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> @@ -24,6 +25,16 @@ <groupId>org.jfxtras</groupId> <artifactId>jfxtras-controls</artifactId> </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-all</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> \ No newline at end of file diff --git a/kieker-trace-diagnosis-application/src/main/java/kieker/diagnosis/service/data/MonitoringLogImporter.java b/kieker-trace-diagnosis-application/src/main/java/kieker/diagnosis/service/data/MonitoringLogImporter.java index 86a23b400cdb3312eeb784d4e188aacdb829ca93..adc683a677b10a9f7f7223bd3dacbe8adee6d121 100644 --- a/kieker-trace-diagnosis-application/src/main/java/kieker/diagnosis/service/data/MonitoringLogImporter.java +++ b/kieker-trace-diagnosis-application/src/main/java/kieker/diagnosis/service/data/MonitoringLogImporter.java @@ -293,7 +293,7 @@ final class MonitoringLogImporter { // Make sure that the timestamp is always in milliseconds if ( DESTINATION_TIMESTAMP_TIME_UNIT != ivSourceTimeUnit ) { - final long newTimestamp = DESTINATION_TIMESTAMP_TIME_UNIT.convert( timestamp, ivSourceTimeUnit ); + final long newTimestamp = DESTINATION_TIMESTAMP_TIME_UNIT.convert( lastMethodCall.getTimestamp( ), ivSourceTimeUnit ); lastMethodCall.setTimestamp( newTimestamp ); } @@ -354,18 +354,18 @@ final class MonitoringLogImporter { try { final Class<?> recordClass = Class.forName( recordName ); final Field sizeField = recordClass.getDeclaredField( "SIZE" ); - size = (byte) sizeField.get( null ); + size = (byte) (int) sizeField.get( null ); ivIgnoredRecordsSizeMap.put( aRecordKey, size ); - ivIgnoredRecords++; } catch ( final Exception ex ) { // We have no chance. We cannot skip the record, as we don't know its size. - throw new RuntimeException( String.format( ivResourceBundle.getString( "errorMessageUnknownRecord" ), recordName ) ); + throw new RuntimeException( String.format( ivResourceBundle.getString( "errorMessageUnknownRecord" ), recordName ), ex ); } } else { size = ivIgnoredRecordsSizeMap.get( aRecordKey ); } + ivIgnoredRecords++; skipBytes( size, aByteBuffer ); } @@ -496,33 +496,44 @@ final class MonitoringLogImporter { @Override public boolean equals( final Object obj ) { - if ( this == obj ) + if ( this == obj ) { return true; - if ( obj == null ) + } + if ( obj == null ) { return false; - if ( getClass( ) != obj.getClass( ) ) + } + if ( getClass( ) != obj.getClass( ) ) { return false; + } final AggregationKey other = (AggregationKey) obj; if ( ivClass == null ) { - if ( other.ivClass != null ) + if ( other.ivClass != null ) { return false; - } else if ( !ivClass.equals( other.ivClass ) ) + } + } else if ( !ivClass.equals( other.ivClass ) ) { return false; + } if ( ivException == null ) { - if ( other.ivException != null ) + if ( other.ivException != null ) { return false; - } else if ( !ivException.equals( other.ivException ) ) + } + } else if ( !ivException.equals( other.ivException ) ) { return false; + } if ( ivHost == null ) { - if ( other.ivHost != null ) + if ( other.ivHost != null ) { return false; - } else if ( !ivHost.equals( other.ivHost ) ) + } + } else if ( !ivHost.equals( other.ivHost ) ) { return false; + } if ( ivMethod == null ) { - if ( other.ivMethod != null ) + if ( other.ivMethod != null ) { return false; - } else if ( !ivMethod.equals( other.ivMethod ) ) + } + } else if ( !ivMethod.equals( other.ivMethod ) ) { return false; + } return true; } diff --git a/kieker-trace-diagnosis-application/src/test/java/kieker/diagnosis/service/data/MonitoringLogServiceTest.java b/kieker-trace-diagnosis-application/src/test/java/kieker/diagnosis/service/data/MonitoringLogServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e122ee7393e414d5266497f464c4356881a892f1 --- /dev/null +++ b/kieker-trace-diagnosis-application/src/test/java/kieker/diagnosis/service/data/MonitoringLogServiceTest.java @@ -0,0 +1,358 @@ +package kieker.diagnosis.service.data; + +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; +import static org.hamcrest.collection.IsEmptyCollection.empty; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.number.OrderingComparison.greaterThan; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.io.IOException; +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.concurrent.TimeUnit; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import com.carrotsearch.hppc.ByteArrayList; +import com.google.common.io.Files; +import com.google.inject.Guice; +import com.google.inject.Injector; + +import kieker.common.record.AbstractMonitoringRecord; +import kieker.common.record.flow.trace.TraceMetadata; +import kieker.common.record.flow.trace.operation.AfterOperationEvent; +import kieker.common.record.flow.trace.operation.AfterOperationFailedEvent; +import kieker.common.record.flow.trace.operation.BeforeOperationEvent; +import kieker.common.record.misc.KiekerMetadataRecord; +import kieker.common.record.system.CPUUtilizationRecord; +import kieker.common.util.registry.IRegistry; +import kieker.common.util.registry.Registry; +import kieker.diagnosis.KiekerTraceDiagnosisModule; + +public class MonitoringLogServiceTest { + + @Rule + public TemporaryFolder ivTemporaryFolder = new TemporaryFolder( ); + + @Rule + public ExpectedException ivExpectedException = ExpectedException.none( ); + + private ByteArrayList ivByteList; + private IRegistry<String> ivStringRegistry; + private MonitoringLogService ivService; + + @Before + public void setUp( ) { + ivByteList = new ByteArrayList( ); + ivStringRegistry = new Registry<>( ); + + final Injector injector = Guice.createInjector( new KiekerTraceDiagnosisModule( ) ); + ivService = injector.getInstance( MonitoringLogService.class ); + } + + @Test + public void testEmptyDirectory( ) { + // Import the directory + final File directory = ivTemporaryFolder.getRoot( ); + ivService.importMonitoringLog( directory ); + + // Make sure that nothing has been imported + assertThat( ivService.getMethods( ), is( empty( ) ) ); + assertThat( ivService.getAggreatedMethods( ), is( empty( ) ) ); + assertThat( ivService.getTraceRoots( ), is( empty( ) ) ); + assertThat( ivService.getProcessedBytes( ), is( 0L ) ); + } + + @Test + public void testSingleTrace( ) throws Exception { + // Prepare the data + writeRecord( new TraceMetadata( 1L, 0L, "0", "host", 0L, 0 ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeMappingFile( ); + finishWriting( ); + + // Import the directory + final File directory = ivTemporaryFolder.getRoot( ); + ivService.importMonitoringLog( directory ); + + // Make sure that the import worked as intended + assertThat( ivService.getMethods( ), hasSize( 2 ) ); + assertThat( ivService.getAggreatedMethods( ), hasSize( 2 ) ); + assertThat( ivService.getTraceRoots( ), hasSize( 1 ) ); + assertThat( ivService.getProcessedBytes( ), is( greaterThan( 0L ) ) ); + } + + @Test + public void testTwoInterleavedTrace( ) throws Exception { + // Prepare the data + writeRecord( new TraceMetadata( 1L, 0L, "0", "host", 0L, 0 ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1" ) ); + writeRecord( new TraceMetadata( 2L, 0L, "0", "host", 0L, 0 ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 2L, 0, "op1", "class2" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1" ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 2L, 0, "op2", "class2" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 2L, 0, "op2", "class2" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 2L, 0, "op1", "class2" ) ); + writeMappingFile( ); + finishWriting( ); + + // Import the directory + final File directory = ivTemporaryFolder.getRoot( ); + ivService.importMonitoringLog( directory ); + + // Make sure that the import worked as intended + assertThat( ivService.getMethods( ), hasSize( 4 ) ); + assertThat( ivService.getAggreatedMethods( ), hasSize( 4 ) ); + assertThat( ivService.getTraceRoots( ), hasSize( 2 ) ); + assertThat( ivService.getProcessedBytes( ), is( greaterThan( 0L ) ) ); + } + + @Test + public void testFailedTrace( ) throws Exception { + // Prepare the data + writeRecord( new TraceMetadata( 1L, 0L, "0", "host", 0L, 0 ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1" ) ); + writeRecord( new AfterOperationFailedEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1", "cause" ) ); + writeRecord( new AfterOperationFailedEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1", "cause" ) ); + writeMappingFile( ); + finishWriting( ); + + // Import the directory + final File directory = ivTemporaryFolder.getRoot( ); + ivService.importMonitoringLog( directory ); + + // Make sure that the import worked as intended + assertThat( ivService.getMethods( ), hasSize( 2 ) ); + assertThat( ivService.getAggreatedMethods( ), hasSize( 2 ) ); + assertThat( ivService.getTraceRoots( ), hasSize( 1 ) ); + assertThat( ivService.getProcessedBytes( ), is( greaterThan( 0L ) ) ); + } + + @Test + public void testMethodAggregation( ) throws Exception { + // Prepare the data + writeRecord( new TraceMetadata( 1L, 0L, "0", "host", 0L, 0 ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1" ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class2" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class2" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeMappingFile( ); + finishWriting( ); + + // Import the directory + final File directory = ivTemporaryFolder.getRoot( ); + ivService.importMonitoringLog( directory ); + + // Make sure that the import worked as intended + assertThat( ivService.getMethods( ), hasSize( 4 ) ); + assertThat( ivService.getAggreatedMethods( ), hasSize( 3 ) ); + assertThat( ivService.getTraceRoots( ), hasSize( 1 ) ); + assertThat( ivService.getProcessedBytes( ), is( greaterThan( 0L ) ) ); + } + + @Test + public void testIncompleteTrace( ) throws Exception { + // Prepare the data + writeRecord( new TraceMetadata( 1L, 0L, "0", "host", 0L, 0 ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1" ) ); + writeMappingFile( ); + finishWriting( ); + + // Import the directory + final File directory = ivTemporaryFolder.getRoot( ); + ivService.importMonitoringLog( directory ); + + // Make sure that the import worked as intended + assertThat( ivService.getMethods( ), hasSize( 0 ) ); + assertThat( ivService.getAggreatedMethods( ), hasSize( 0 ) ); + assertThat( ivService.getTraceRoots( ), hasSize( 0 ) ); + assertThat( ivService.getIncompleteTraces( ), is( 1 ) ); + assertThat( ivService.getProcessedBytes( ), is( greaterThan( 0L ) ) ); + } + + @Test + public void testDanglingRecords( ) throws Exception { + // Prepare the data + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeRecord( new BeforeOperationEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 1L, 0, "op2", "class1" ) ); + writeRecord( new AfterOperationEvent( System.currentTimeMillis( ), 1L, 0, "op1", "class1" ) ); + writeMappingFile( ); + finishWriting( ); + + // Import the directory + final File directory = ivTemporaryFolder.getRoot( ); + ivService.importMonitoringLog( directory ); + + // Make sure that the import worked as intended + assertThat( ivService.getMethods( ), hasSize( 0 ) ); + assertThat( ivService.getAggreatedMethods( ), hasSize( 0 ) ); + assertThat( ivService.getTraceRoots( ), hasSize( 0 ) ); + assertThat( ivService.getDanglingRecords( ), is( 4 ) ); + assertThat( ivService.getProcessedBytes( ), is( greaterThan( 0L ) ) ); + } + + @Test + public void testDurationConversion( ) throws Exception { + // Prepare the data + writeRecord( new TraceMetadata( 1L, 0L, "0", "host", 0L, 0 ) ); + writeRecord( new KiekerMetadataRecord( "0", "0", "0", 0, false, 0L, TimeUnit.SECONDS.name( ), 0 ) ); + writeRecord( new BeforeOperationEvent( 10, 1L, 0, "op1", "class1" ) ); + writeRecord( new AfterOperationFailedEvent( 20, 1L, 0, "op1", "class1", "cause" ) ); + writeMappingFile( ); + finishWriting( ); + + // Import the directory + final File directory = ivTemporaryFolder.getRoot( ); + ivService.importMonitoringLog( directory ); + + // Make sure that the import worked as intended + final MethodCall methodCall = ivService.getMethods( ).get( 0 ); + assertThat( methodCall.getDuration( ), is( 10000000000L ) ); + } + + @Test + public void testTimestampConversion( ) throws Exception { + // Prepare the data + writeRecord( new TraceMetadata( 1L, 0L, "0", "host", 0L, 0 ) ); + writeRecord( new KiekerMetadataRecord( "0", "0", "0", 0, false, 0L, TimeUnit.SECONDS.name( ), 0 ) ); + writeRecord( new BeforeOperationEvent( 10, 1L, 0, "op1", "class1" ) ); + writeRecord( new AfterOperationFailedEvent( 20, 1L, 0, "op1", "class1", "cause" ) ); + writeMappingFile( ); + finishWriting( ); + + // Import the directory + final File directory = ivTemporaryFolder.getRoot( ); + ivService.importMonitoringLog( directory ); + + // Make sure that the import worked as intended + final MethodCall methodCall = ivService.getMethods( ).get( 0 ); + assertThat( methodCall.getTimestamp( ), is( 10000L ) ); + } + + @Test + public void testIgnoreRecord( ) throws Exception { + // Prepare the data + writeRecord( new CPUUtilizationRecord( 0L, "", "", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ) ); + writeRecord( new CPUUtilizationRecord( 1L, "", "", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ) ); + + writeMappingFile( ); + finishWriting( ); + + // Import the directory + final File directory = ivTemporaryFolder.getRoot( ); + ivService.importMonitoringLog( directory ); + + // Make sure that the import worked as intended + assertThat( ivService.getMethods( ), hasSize( 0 ) ); + assertThat( ivService.getIgnoredRecords( ), is( 2 ) ); + } + + @Test + public void testUnknownRecord( ) throws Exception { + // Prepare the data + writeRecord( new UnknownRecord( ) ); + + writeMappingFile( ); + finishWriting( ); + + // The import should not work + ivExpectedException.expect( RuntimeException.class ); + + // Import the directory + final File directory = ivTemporaryFolder.getRoot( ); + ivService.importMonitoringLog( directory ); + } + + private void writeRecord( final AbstractMonitoringRecord aRecord ) { + // Register the record name + final int recordKey = ivStringRegistry.get( aRecord.getClass( ).getName( ) ); + + // Register the record's strings + aRecord.registerStrings( ivStringRegistry ); + + // Now write the record into our buffer + final byte[] byteArray = new byte[aRecord.getSize( ) + 4 + 8]; + final ByteBuffer byteBuffer = ByteBuffer.wrap( byteArray ); + byteBuffer.putInt( recordKey ); + byteBuffer.putLong( System.currentTimeMillis( ) ); + aRecord.writeBytes( byteBuffer, ivStringRegistry ); + byteBuffer.flip( ); + + ivByteList.add( byteArray ); + } + + private void writeMappingFile( ) throws IOException { + // Collect the mappings + final StringBuilder stringBuilder = new StringBuilder( ); + + final Object[] allStrings = ivStringRegistry.getAll( ); + for ( final Object string : allStrings ) { + final int id = ivStringRegistry.get( (String) string ); + stringBuilder.append( "$" ).append( id ).append( "=" ).append( string ).append( "\n" ); + } + + // Write the mapping file + final File mappingFile = new File( ivTemporaryFolder.getRoot( ), "kieker.map" ); + Files.write( stringBuilder.toString( ), mappingFile, Charset.forName( "UTF-8" ) ); + } + + private void finishWriting( ) throws IOException { + final File binaryFile = new File( ivTemporaryFolder.getRoot( ), "kieker.bin" ); + ivByteList.trimToSize( ); + Files.write( ivByteList.buffer, binaryFile ); + } + + private static class UnknownRecord extends AbstractMonitoringRecord { + + private static final long serialVersionUID = 1L; + + @Override + public Object[] toArray( ) { + return null; + } + + @Override + public void writeBytes( final ByteBuffer aBuffer, final IRegistry<String> aStringRegistry ) throws BufferOverflowException { + } + + @Override + public void initFromBytes( final ByteBuffer aBuffer, final IRegistry<String> aStringRegistry ) throws BufferUnderflowException { + } + + @Override + public void initFromArray( final Object[] aValues ) { + } + + @Override + public Class<?>[] getValueTypes( ) { + return null; + } + + @Override + public int getSize( ) { + return 0; + } + + } + +} diff --git a/kieker-trace-diagnosis-architecture/pom.xml b/kieker-trace-diagnosis-architecture/pom.xml index 48832f34c97399ed3042dca05995d1d4d56a7d6a..679a86b405a0878f8ae25bf87905bd33f0413800 100644 --- a/kieker-trace-diagnosis-architecture/pom.xml +++ b/kieker-trace-diagnosis-architecture/pom.xml @@ -33,9 +33,9 @@ <artifactId>junit</artifactId> <scope>test</scope> </dependency> - <dependency> + <dependency> <groupId>org.hamcrest</groupId> - <artifactId>hamcrest-core</artifactId> + <artifactId>hamcrest-all</artifactId> <scope>test</scope> </dependency> </dependencies> diff --git a/kieker-trace-diagnosis-dependencies/pom.xml b/kieker-trace-diagnosis-dependencies/pom.xml index 9fb90b9288b397b591190b5c797ffad13a297a4f..cb13c1a91779ab136d3b594686f68401f9de3375 100644 --- a/kieker-trace-diagnosis-dependencies/pom.xml +++ b/kieker-trace-diagnosis-dependencies/pom.xml @@ -66,7 +66,7 @@ </dependency> <dependency> <groupId>org.hamcrest</groupId> - <artifactId>hamcrest-core</artifactId> + <artifactId>hamcrest-all</artifactId> <version>1.3</version> </dependency> <dependency>