Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import com.datadog.debugger.sink.SymbolSink;
import datadog.instrument.utils.ClassNameTrie;
import datadog.trace.api.internal.VisibleForTesting;
import datadog.trace.bootstrap.debugger.DebuggerContext;
import datadog.trace.util.AgentTaskScheduler;
import datadog.trace.util.Strings;
Expand All @@ -28,6 +29,7 @@
import java.util.concurrent.TimeUnit;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -206,7 +208,7 @@ public void scanJar(
scanDirectory(jarPath, alreadyScannedJars, baos, buffer, symDBReport);
} else {
try {
try (JarFile jarFile = new JarFile(jarPathFile)) {
try (JarFile jarFile = openJarFile(jarPathFile)) {
jarFile.stream()
.filter(jarEntry -> jarEntry.getName().endsWith(".class"))
.filter(
Expand All @@ -225,14 +227,19 @@ public void scanJar(
alreadyScannedJars.add(jarPath.toString());
}

@VisibleForTesting
JarFile openJarFile(File file) throws IOException {
return new JarFile(file);
}

private void scanDirectory(
Path jarPath,
Set<String> alreadyScannedJars,
ByteArrayOutputStream baos,
byte[] buffer,
SymDBReport symDBReport) {
try {
Files.walk(jarPath)
try (Stream<Path> paths = Files.walk(jarPath)) {
paths
// explicitly no follow links walking the directory to avoid cycles
.filter(path -> Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS))
.filter(path -> path.toString().endsWith(".class"))
Expand Down Expand Up @@ -276,11 +283,14 @@ private void parseJarEntry(
byte[] buffer) {
LOGGER.debug("parsing jarEntry class: {}", jarEntry.getName());
try {
InputStream inputStream = jarFile.getInputStream(jarEntry);
int readBytes;
baos.reset();
while ((readBytes = inputStream.read(buffer)) != -1) {
baos.write(buffer, 0, readBytes);
// must be closed so the JarFile returns its Inflater to the cache instead of allocating a
// new native zlib context per entry
try (InputStream inputStream = jarFile.getInputStream(jarEntry)) {
int readBytes;
baos.reset();
while ((readBytes = inputStream.read(buffer)) != -1) {
baos.write(buffer, 0, readBytes);
}
}
parseClass(symDBReport, jarEntry.getName(), baos.toByteArray(), jarPath.toString());
} catch (IOException ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,19 @@

import com.datadog.debugger.sink.SymbolSink;
import com.datadog.debugger.util.ClassNameFiltering;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import org.mockito.ArgumentCaptor;
Expand Down Expand Up @@ -76,6 +86,45 @@ void testScanQueuedCorruptedJars() {
captor.getAllValues().get(2));
}

@Test
void testScanJarClosesEntryStreams() throws Exception {
SymbolSink symbolSink = mock(SymbolSink.class);
CountingJarFile[] holder = new CountingJarFile[1];
SymbolAggregator symbolAggregator =
new SymbolAggregator(ClassNameFiltering.allowAll(), emptyList(), symbolSink, 1) {
@Override
JarFile openJarFile(File file) throws IOException {
return holder[0] = new CountingJarFile(file);
}
};
Path jarPath = Paths.get(getClass().getResource("/debugger-symbol.jar").toURI());
symbolAggregator.scanJar(
SymDBReport.NO_OP, jarPath, new ByteArrayOutputStream(8192), new byte[4096]);
assertTrue(holder[0].opened.get() > 0);
assertEquals(holder[0].opened.get(), holder[0].closed.get());
}

private static class CountingJarFile extends JarFile {
final AtomicInteger opened = new AtomicInteger();
final AtomicInteger closed = new AtomicInteger();

CountingJarFile(File file) throws IOException {
super(file);
}

@Override
public synchronized InputStream getInputStream(ZipEntry ze) throws IOException {
opened.incrementAndGet();
return new FilterInputStream(super.getInputStream(ze)) {
@Override
public void close() throws IOException {
closed.incrementAndGet();
super.close();
}
};
}
}

@Test
@DisabledIf(
value = "datadog.environment.JavaVirtualMachine#isJ9",
Expand Down