Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/src/main/java/com/microsoft/gctoolkit/GCToolKit.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.microsoft.gctoolkit.aggregator.EventSource;
import com.microsoft.gctoolkit.io.DataSource;
import com.microsoft.gctoolkit.io.GCLogFile;
import com.microsoft.gctoolkit.io.LogFileReadLimitExceededException;
import com.microsoft.gctoolkit.io.RotatingGCLogFile;
import com.microsoft.gctoolkit.io.SingleGCLogFile;
import com.microsoft.gctoolkit.jvm.Diary;
Expand All @@ -16,6 +17,7 @@
import com.microsoft.gctoolkit.message.JVMEventChannel;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
Expand Down Expand Up @@ -312,6 +314,10 @@ public JavaVirtualMachine analyze(DataSource<?> dataSource) throws IOException
long start = System.currentTimeMillis();
javaVirtualMachine.analyze(filteredAggregators, jvmEventChannel, dataSourceChannel);
LOGGER.log(Level.FINE,() -> "Analysis completed in " + (System.currentTimeMillis() - start) + "ms");
} catch (LogFileReadLimitExceededException limitExceeded) {
throw limitExceeded;
} catch (UncheckedIOException uncheckedIOException) {
throw uncheckedIOException.getCause();
} catch(Throwable t) {
LOGGER.log(Level.SEVERE, "Internal Error: Cannot invoke analyze method", t);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package com.microsoft.gctoolkit.io;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;

final class ArchiveSliceInputStream extends InputStream {

private final FileInputStream inputStream;
private long remaining;

ArchiveSliceInputStream(Path path, long offset, long length) throws IOException {
inputStream = new FileInputStream(path.toFile());
try {
inputStream.getChannel().position(offset);
remaining = length;
} catch (IOException | RuntimeException | Error failure) {

Check warning on line 20 in api/src/main/java/com/microsoft/gctoolkit/io/ArchiveSliceInputStream.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Catch Exception instead of Error.

See more on https://sonarcloud.io/project/issues?id=microsoft_gctoolkit&issues=AaA-k8w-18hWDt2OQ7Fo&open=AaA-k8w-18hWDt2OQ7Fo&pullRequest=583
try {
inputStream.close();
} catch (IOException closeException) {
failure.addSuppressed(closeException);
}
throw failure;
}
}

@Override
public int read() throws IOException {
if (remaining == 0L) {
return -1;
}
int value = inputStream.read();
if (value != -1) {
remaining--;
}
return value;
}

@Override
public int read(byte[] bytes, int offset, int length) throws IOException {
if (remaining == 0L) {
return -1;
}
int count = inputStream.read(bytes, offset, (int) Math.min(length, remaining));
if (count > 0) {
remaining -= count;
}
return count;
}

@Override
public long skip(long count) throws IOException {
long skipped = inputStream.skip(Math.min(count, remaining));
remaining -= skipped;
return skipped;
}

@Override
public void close() throws IOException {
inputStream.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package com.microsoft.gctoolkit.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Spliterator;
import java.util.function.Consumer;

final class BoundedLineSpliterator implements Spliterator<String> {

private static final int NO_PENDING_CHARACTER = -2;

private final BufferedReader reader;
private final int maximumLineCharacters;
private final Path path;
private final String archiveEntry;
private int pendingCharacter = NO_PENDING_CHARACTER;
private boolean closed;

BoundedLineSpliterator(
Reader reader,
int maximumLineCharacters,
Path path,
String archiveEntry) {
this.reader = new BufferedReader(reader);
this.maximumLineCharacters = maximumLineCharacters;
this.path = path;
this.archiveEntry = archiveEntry;
}

@Override
public boolean tryAdvance(Consumer<? super String> action) {

Check failure on line 37 in api/src/main/java/com/microsoft/gctoolkit/io/BoundedLineSpliterator.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 22 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=microsoft_gctoolkit&issues=AaA-k8wm18hWDt2OQ7Fl&open=AaA-k8wm18hWDt2OQ7Fl&pullRequest=583
Objects.requireNonNull(action);
if (closed) {
return false;
}

StringBuilder line = new StringBuilder(Math.min(maximumLineCharacters, 8192));
try {
while (true) {
int character = readCharacter();
if (character == -1) {
close();
if (line.length() == 0) {
return false;
}
action.accept(line.toString());
return true;
}
if (character == '\n') {
action.accept(line.toString());
return true;
}
if (character == '\r') {
int following = readCharacter();
if (following != '\n' && following != -1) {
pendingCharacter = following;
}
action.accept(line.toString());
if (following == -1) {
close();
}
return true;
}
if (line.length() == maximumLineCharacters) {
throw new LogFileReadLimitExceededException(
LogFileReadLimitExceededException.LimitType.LINE_CHARACTERS,
path,
archiveEntry,
Integer.toString(maximumLineCharacters),
Integer.toString(maximumLineCharacters + 1));
}
line.append((char) character);
}
} catch (IOException exception) {
closeAfterFailure(exception);
throw new UncheckedIOException(exception);
} catch (RuntimeException exception) {
closeAfterFailure(exception);
throw exception;
}
}

private int readCharacter() throws IOException {
if (pendingCharacter != NO_PENDING_CHARACTER) {
int character = pendingCharacter;
pendingCharacter = NO_PENDING_CHARACTER;
return character;
}
return reader.read();
}

private void closeAfterFailure(Throwable failure) {
try {
close();
} catch (IOException closeException) {
failure.addSuppressed(closeException);
}
}

void closeUnchecked() {
try {
close();
} catch (IOException exception) {
throw new UncheckedIOException(exception);
}
}

private void close() throws IOException {
if (!closed) {
closed = true;
reader.close();
}
}

@Override
public Spliterator<String> trySplit() {
return null;
}

@Override
public long estimateSize() {
return Long.MAX_VALUE;
}

@Override
public int characteristics() {
return ORDERED | NONNULL;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package com.microsoft.gctoolkit.io;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;

final class CountingInputStream extends FilterInputStream {

private final long maximumBytes;
private final Path path;
private long bytesRead;

CountingInputStream(InputStream inputStream, long maximumBytes, Path path) {
super(inputStream);
this.maximumBytes = maximumBytes;
this.path = path;
}

long getBytesRead() {
return bytesRead;
}

@Override
public int read() throws IOException {
int value = super.read();
if (value != -1) {
record(1L);
}
return value;
}

@Override
public int read(byte[] bytes, int offset, int length) throws IOException {
int count = super.read(bytes, offset, nextReadLength(length));
if (count > 0) {
record(count);
}
return count;
}

@Override
public long skip(long count) throws IOException {
long skipped = super.skip(nextReadLength(count));
record(skipped);
return skipped;
}

private int nextReadLength(int requestedLength) {
return (int) nextReadLength((long) requestedLength);
}

private long nextReadLength(long requestedLength) {
long remaining = maximumBytes - bytesRead;
long detectableLength = remaining == Long.MAX_VALUE ? Long.MAX_VALUE : remaining + 1L;
return Math.min(requestedLength, detectableLength);
}

private void record(long count) {

Check warning on line 61 in api/src/main/java/com/microsoft/gctoolkit/io/CountingInputStream.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this method to not match a restricted identifier.

See more on https://sonarcloud.io/project/issues?id=microsoft_gctoolkit&issues=AaA-k8w218hWDt2OQ7Fn&open=AaA-k8w218hWDt2OQ7Fn&pullRequest=583
if (count <= 0L) {
return;
}
if (count > maximumBytes - bytesRead) {
long observed = bytesRead == Long.MAX_VALUE
? Long.MAX_VALUE
: bytesRead + count;
throw new LogFileReadLimitExceededException(
LogFileReadLimitExceededException.LimitType.COMPRESSED_BYTES,
path,
null,
Long.toString(maximumBytes),
Long.toString(observed));
}
bytesRead += count;
}
}
2 changes: 2 additions & 0 deletions api/src/main/java/com/microsoft/gctoolkit/io/DataSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public interface DataSource<T> {
* @return A stream of the data.
* @throws IOException Thrown if the data cannot be streamed,
* or an IOException is raised while streaming.
* @throws LogFileReadLimitExceededException if a log-file resource limit is exceeded during a
* lazy terminal operation
*/
Stream<T> stream() throws IOException;

Expand Down
35 changes: 28 additions & 7 deletions api/src/main/java/com/microsoft/gctoolkit/io/GCLogFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,33 @@ public abstract class GCLogFile extends FileDataSource<String> {
private Diary diary;
private TripleState unifiedFormat = TripleState.UNKNOWN;
private JavaVirtualMachine jvm = null;
private final LogFileReadLimits readLimits;

/**
* Subclass only.
* @param path The path to the GCLogFile or, in the case of rotating log files, the parent directory.
*/
protected GCLogFile(Path path) {
this(path, LogFileReadLimits.defaults());
}

/**
* Subclass only.
* @param path The path to the GCLogFile or, in the case of rotating log files, the parent directory.
* @param readLimits resource limits applied while streaming the log
*/
protected GCLogFile(Path path, LogFileReadLimits readLimits) {
super(path);
this.readLimits = Objects.requireNonNull(readLimits, "readLimits");
}

/**
* Returns the resource limits applied while streaming this log.
*
* @return immutable read limits
*/
public final LogFileReadLimits getReadLimits() {
return readLimits;
}

/**
Expand Down Expand Up @@ -96,13 +116,14 @@ private Diarizer diarizer() {
public Diary diary() throws IOException {
if ( diary == null) {
Diarizer diarizer = diarizer();
stream()
.filter(Objects::nonNull)
.map(String::trim)
.filter(s -> s.length() > 0)
.map(diarizer::diarize)
.filter(completed -> completed)
.findFirst();
try (Stream<String> lines = stream()) {
lines.filter(Objects::nonNull)
.map(String::trim)
.filter(s -> s.length() > 0)
.map(diarizer::diarize)
.filter(completed -> completed)
.findFirst();
}
this.diary = diarizer.getDiary();
}
return diary;
Expand Down
Loading
Loading