From b0ff8613923f6bb88cb2438606789fac8e91a72d Mon Sep 17 00:00:00 2001 From: Nikolay Martynov Date: Wed, 14 Aug 2019 16:38:53 -0400 Subject: [PATCH] Do not trace openConnection for Datadog ClassLoader --- .../trace/bootstrap/DatadogClassLoader.java | 107 ----------------- .../bootstrap/InternalJarURLHandler.java | 111 ++++++++++++++++++ .../UrlInstrumentation.java | 12 +- 3 files changed, 120 insertions(+), 110 deletions(-) create mode 100644 dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/InternalJarURLHandler.java diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java index 3ba40d6e17..1317cc4414 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/DatadogClassLoader.java @@ -1,20 +1,8 @@ package datadog.trace.bootstrap; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.net.URLConnection; -import java.net.URLStreamHandler; -import java.nio.file.NoSuchFileException; -import java.security.Permission; -import java.util.HashMap; -import java.util.Map; -import java.util.jar.JarEntry; -import java.util.jar.JarInputStream; import lombok.extern.slf4j.Slf4j; /** @@ -116,99 +104,4 @@ public class DatadogClassLoader extends URLClassLoader { throw new ClassNotFoundException(name); } } - - protected static class InternalJarURLHandler extends URLStreamHandler { - private final Map filenameToBytes = new HashMap<>(); - - public InternalJarURLHandler( - final String internalJarFileName, final ClassLoader classloaderForJarResource) { - - // "/" is used as the default url of the jar - // This is called by the SecureClassLoader trying to obtain permissions - filenameToBytes.put("/", new byte[] {}); - - final InputStream jarStream = - classloaderForJarResource.getResourceAsStream(internalJarFileName); - - if (jarStream != null) { - try (final JarInputStream inputStream = new JarInputStream(jarStream)) { - JarEntry entry = inputStream.getNextJarEntry(); - - while (entry != null) { - filenameToBytes.put("/" + entry.getName(), getBytes(inputStream)); - - entry = inputStream.getNextJarEntry(); - } - - } catch (final IOException e) { - log.error("Unable to read internal jar", e); - } - } else { - log.error("Internal jar not found"); - } - } - - @Override - protected URLConnection openConnection(final URL url) throws IOException { - final byte[] bytes = filenameToBytes.get(url.getFile()); - - if (bytes == null) { - throw new NoSuchFileException(url.getFile(), null, url.getFile() + " not in internal jar"); - } - - return new InternalJarURLConnection(url, bytes); - } - } - - protected static class InternalJarURLConnection extends URLConnection { - private final byte[] bytes; - - private InternalJarURLConnection(final URL url, final byte[] bytes) { - super(url); - this.bytes = bytes; - } - - @Override - public void connect() throws IOException { - connected = true; - } - - @Override - public InputStream getInputStream() throws IOException { - return new ByteArrayInputStream(bytes); - } - - @Override - public Permission getPermission() { - // No permissions needed because all classes are in memory - return null; - } - } - - /** - * Standard "copy InputStream to byte[]" implementation using a ByteArrayOutputStream - * - *

IOUtils.toByteArray() or Java 9's InputStream.readAllBytes() could be replacements if they - * were available - * - *

This can be optimized using the JarEntry's size(), but its not always available - * - * @param inputStream - * @return a byte[] from the inputstream - * @throws IOException - */ - private static byte[] getBytes(final InputStream inputStream) throws IOException { - final byte[] buffer = new byte[4096]; - - int bytesRead = inputStream.read(buffer, 0, buffer.length); - try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - while (bytesRead != -1) { - outputStream.write(buffer, 0, bytesRead); - - bytesRead = inputStream.read(buffer, 0, buffer.length); - } - - return outputStream.toByteArray(); - } - } } diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/InternalJarURLHandler.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/InternalJarURLHandler.java new file mode 100644 index 0000000000..a634593c5e --- /dev/null +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/InternalJarURLHandler.java @@ -0,0 +1,111 @@ +package datadog.trace.bootstrap; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.nio.file.NoSuchFileException; +import java.security.Permission; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class InternalJarURLHandler extends URLStreamHandler { + private final Map filenameToBytes = new HashMap<>(); + + InternalJarURLHandler( + final String internalJarFileName, final ClassLoader classloaderForJarResource) { + + // "/" is used as the default url of the jar + // This is called by the SecureClassLoader trying to obtain permissions + filenameToBytes.put("/", new byte[] {}); + + final InputStream jarStream = + classloaderForJarResource.getResourceAsStream(internalJarFileName); + + if (jarStream != null) { + try (final JarInputStream inputStream = new JarInputStream(jarStream)) { + JarEntry entry = inputStream.getNextJarEntry(); + + while (entry != null) { + filenameToBytes.put("/" + entry.getName(), getBytes(inputStream)); + + entry = inputStream.getNextJarEntry(); + } + + } catch (final IOException e) { + log.error("Unable to read internal jar", e); + } + } else { + log.error("Internal jar not found"); + } + } + + @Override + protected URLConnection openConnection(final URL url) throws IOException { + final byte[] bytes = filenameToBytes.get(url.getFile()); + + if (bytes == null) { + throw new NoSuchFileException(url.getFile(), null, url.getFile() + " not in internal jar"); + } + + return new InternalJarURLConnection(url, bytes); + } + + /** + * Standard "copy InputStream to byte[]" implementation using a ByteArrayOutputStream + * + *

IOUtils.toByteArray() or Java 9's InputStream.readAllBytes() could be replacements if they + * were available + * + *

This can be optimized using the JarEntry's size(), but its not always available + * + * @param inputStream stream to read + * @return a byte[] from the inputstream + */ + private static byte[] getBytes(final InputStream inputStream) throws IOException { + final byte[] buffer = new byte[4096]; + + int bytesRead = inputStream.read(buffer, 0, buffer.length); + try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + while (bytesRead != -1) { + outputStream.write(buffer, 0, bytesRead); + + bytesRead = inputStream.read(buffer, 0, buffer.length); + } + + return outputStream.toByteArray(); + } + } + + private static class InternalJarURLConnection extends URLConnection { + private final byte[] bytes; + + private InternalJarURLConnection(final URL url, final byte[] bytes) { + super(url); + this.bytes = bytes; + } + + @Override + public void connect() throws IOException { + connected = true; + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(bytes); + } + + @Override + public Permission getPermission() { + // No permissions needed because all classes are in memory + return null; + } + } +} diff --git a/dd-java-agent/instrumentation/http-url-connection/src/main/java/datadog/trace/instrumentation/http_url_connection/UrlInstrumentation.java b/dd-java-agent/instrumentation/http-url-connection/src/main/java/datadog/trace/instrumentation/http_url_connection/UrlInstrumentation.java index 1f6ff9e035..d1da1fb685 100644 --- a/dd-java-agent/instrumentation/http-url-connection/src/main/java/datadog/trace/instrumentation/http_url_connection/UrlInstrumentation.java +++ b/dd-java-agent/instrumentation/http-url-connection/src/main/java/datadog/trace/instrumentation/http_url_connection/UrlInstrumentation.java @@ -12,11 +12,13 @@ import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.api.Config; import datadog.trace.api.DDSpanTypes; import datadog.trace.api.DDTags; +import datadog.trace.bootstrap.InternalJarURLHandler; import io.opentracing.Scope; import io.opentracing.Span; import io.opentracing.tag.Tags; import io.opentracing.util.GlobalTracer; import java.net.URL; +import java.net.URLStreamHandler; import java.util.Collections; import java.util.Map; import net.bytebuddy.asm.Advice; @@ -49,10 +51,14 @@ public class UrlInstrumentation extends Instrumenter.Default { @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void errorSpan( - @Advice.This final URL url, @Advice.Thrown final Throwable throwable) { + @Advice.This final URL url, + @Advice.Thrown final Throwable throwable, + @Advice.FieldValue("handler") final URLStreamHandler handler) { if (throwable != null) { - final boolean isTraceRequest = Thread.currentThread().getName().equals("dd-agent-writer"); - if (isTraceRequest) { + // Various agent components end up calling `openConnection` indirectly + // when loading classes. Avoid tracing these calls. + final boolean disableTracing = handler instanceof InternalJarURLHandler; + if (disableTracing) { return; }