diff --git a/dd-java-agent/agent-ci-visibility/build.gradle b/dd-java-agent/agent-ci-visibility/build.gradle index 9917f17cbdb..2f615cc76ae 100644 --- a/dd-java-agent/agent-ci-visibility/build.gradle +++ b/dd-java-agent/agent-ci-visibility/build.gradle @@ -27,11 +27,12 @@ excludedClassesCoverage += [ "datadog.trace.civisibility.ci.CIInfo", "datadog.trace.civisibility.ci.CIInfo.Builder", "datadog.trace.civisibility.communication.*", - "datadog.trace.civisibility.config.JvmInfoFactory", + "datadog.trace.civisibility.config.CachingJvmInfoFactory", + "datadog.trace.civisibility.config.JvmInfoFactoryImpl", + "datadog.trace.civisibility.config.JvmInfoFactoryImpl.JvmVersionOutputParser", "datadog.trace.civisibility.config.ConfigurationApi", "datadog.trace.civisibility.config.ModuleExecutionSettingsFactory", "datadog.trace.civisibility.config.JvmInfo", - "datadog.trace.civisibility.config.JvmInfoFactory.JvmVersionOutputParser", "datadog.trace.civisibility.config.CachingModuleExecutionSettingsFactory", "datadog.trace.civisibility.config.CachingModuleExecutionSettingsFactory.Key", "datadog.trace.civisibility.config.CiVisibilitySettings", diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java index 1c3000726fa..70a1555bb67 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/CiVisibilitySystem.java @@ -17,10 +17,12 @@ import datadog.trace.civisibility.codeowners.CodeownersProvider; import datadog.trace.civisibility.communication.BackendApi; import datadog.trace.civisibility.communication.BackendApiFactory; +import datadog.trace.civisibility.config.CachingJvmInfoFactory; import datadog.trace.civisibility.config.CachingModuleExecutionSettingsFactory; import datadog.trace.civisibility.config.ConfigurationApi; import datadog.trace.civisibility.config.ConfigurationApiImpl; import datadog.trace.civisibility.config.JvmInfoFactory; +import datadog.trace.civisibility.config.JvmInfoFactoryImpl; import datadog.trace.civisibility.config.ModuleExecutionSettingsFactory; import datadog.trace.civisibility.config.ModuleExecutionSettingsFactoryImpl; import datadog.trace.civisibility.coverage.CoverageProbeStoreFactory; @@ -119,10 +121,11 @@ private static BuildEventsHandler.Factory buildEventsHandlerFactory( DDBuildSystemSession.Factory sessionFactory = buildSystemSessionFactory( config, sco, gitInfoProvider, coverageProbeStoreFactory, gitClientFactory); + JvmInfoFactory jvmInfoFactory = new CachingJvmInfoFactory(config, new JvmInfoFactoryImpl()); return new BuildEventsHandler.Factory() { @Override public BuildEventsHandler create() { - return new BuildEventsHandlerImpl<>(sessionFactory, new JvmInfoFactory()); + return new BuildEventsHandlerImpl<>(sessionFactory, jvmInfoFactory); } }; } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CachingJvmInfoFactory.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CachingJvmInfoFactory.java new file mode 100644 index 00000000000..62204a42aec --- /dev/null +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/CachingJvmInfoFactory.java @@ -0,0 +1,23 @@ +package datadog.trace.civisibility.config; + +import datadog.trace.api.Config; +import datadog.trace.api.cache.DDCache; +import datadog.trace.api.cache.DDCaches; +import java.nio.file.Path; + +public class CachingJvmInfoFactory implements JvmInfoFactory { + + private final DDCache cache; + private final JvmInfoFactoryImpl delegate; + + public CachingJvmInfoFactory(Config config, JvmInfoFactoryImpl delegate) { + this.delegate = delegate; + this.cache = + DDCaches.newFixedSizeCache(config.getCiVisibilityModuleExecutionSettingsCacheSize()); + } + + @Override + public JvmInfo getJvmInfo(Path jvmExecutablePath) { + return cache.computeIfAbsent(jvmExecutablePath, delegate::getJvmInfo); + } +} diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/JvmInfoFactory.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/JvmInfoFactory.java index 6a62ce3927d..99a26b3a885 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/JvmInfoFactory.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/JvmInfoFactory.java @@ -1,89 +1,7 @@ package datadog.trace.civisibility.config; -import datadog.trace.civisibility.utils.ShellCommandExecutor; -import datadog.trace.util.ProcessUtils; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.Charset; import java.nio.file.Path; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -public class JvmInfoFactory { - - private static final Logger LOGGER = LoggerFactory.getLogger(JvmInfoFactory.class); - - private static final int JVM_VERSION_LAUNCH_TIMEOUT = 5_000; - - // provide some confidence - public JvmInfo getJvmInfo(Path jvmExecutablePath) { - String currentJvm = - ProcessUtils.getCurrentJvmPath(); // might be home dir or full executable path - // if we cannot determine forked JVM, - // we assume it is the same as current one, - // which is the most common case - if (jvmExecutablePath == null - || currentJvm != null && jvmExecutablePath.startsWith(currentJvm)) { - return JvmInfo.CURRENT_JVM; - } else { - return doGetJvmInfo(jvmExecutablePath); - } - } - - static JvmInfo doGetJvmInfo(Path jvmExecutablePath) { - Path jvmExecutableFolder = jvmExecutablePath.getParent(); - ShellCommandExecutor commandExecutor = - new ShellCommandExecutor(jvmExecutableFolder.toFile(), JVM_VERSION_LAUNCH_TIMEOUT); - try { - return commandExecutor.executeCommandReadingError( - new JvmVersionOutputParser(), "./java", "-XshowSettings:properties", "-version"); - - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - LOGGER.warn( - "Interrupted while waiting for JVM runtime info for {}, assuming {}", - jvmExecutablePath, - JvmInfo.CURRENT_JVM); - return JvmInfo.CURRENT_JVM; - - } catch (Exception e) { - LOGGER.warn( - "Could not determine JVM runtime info for {}, assuming {}", - jvmExecutablePath, - JvmInfo.CURRENT_JVM, - e); - return JvmInfo.CURRENT_JVM; - } - } - - private static final class JvmVersionOutputParser - implements ShellCommandExecutor.OutputParser { - @Override - public JvmInfo parse(InputStream inputStream) throws IOException { - String name = null; - String version = null; - String vendor = null; - - BufferedReader bis = - new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset())); - String line; - while ((line = bis.readLine()) != null) { - if (line.contains("java.runtime.name ")) { - name = getPropertyValue(line); - } else if (line.contains("java.version ")) { - version = getPropertyValue(line); - } else if (line.contains("java.vendor ")) { - vendor = getPropertyValue(line); - } - } - return new JvmInfo(name, version, vendor); - } - - private String getPropertyValue(String line) { - // format of the input is: " property.name = propertyValue" - return line.substring(line.indexOf('=') + 2); - } - } +public interface JvmInfoFactory { + JvmInfo getJvmInfo(Path jvmExecutablePath); } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/JvmInfoFactoryImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/JvmInfoFactoryImpl.java new file mode 100644 index 00000000000..0a3a4a28519 --- /dev/null +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/config/JvmInfoFactoryImpl.java @@ -0,0 +1,89 @@ +package datadog.trace.civisibility.config; + +import datadog.trace.civisibility.utils.ShellCommandExecutor; +import datadog.trace.util.ProcessUtils; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.nio.file.Path; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JvmInfoFactoryImpl implements JvmInfoFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(JvmInfoFactoryImpl.class); + + private static final int JVM_VERSION_LAUNCH_TIMEOUT = 5_000; + + @Override + public JvmInfo getJvmInfo(Path jvmExecutablePath) { + String currentJvm = + ProcessUtils.getCurrentJvmPath(); // might be home dir or full executable path + // if we cannot determine forked JVM, + // we assume it is the same as current one, + // which is the most common case + if (jvmExecutablePath == null + || currentJvm != null && jvmExecutablePath.startsWith(currentJvm)) { + return JvmInfo.CURRENT_JVM; + } else { + return doGetJvmInfo(jvmExecutablePath); + } + } + + static JvmInfo doGetJvmInfo(Path jvmExecutablePath) { + Path jvmExecutableFolder = jvmExecutablePath.getParent(); + ShellCommandExecutor commandExecutor = + new ShellCommandExecutor(jvmExecutableFolder.toFile(), JVM_VERSION_LAUNCH_TIMEOUT); + try { + return commandExecutor.executeCommandReadingError( + new JvmVersionOutputParser(), "./java", "-XshowSettings:properties", "-version"); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.warn( + "Interrupted while waiting for JVM runtime info for {}, assuming {}", + jvmExecutablePath, + JvmInfo.CURRENT_JVM); + return JvmInfo.CURRENT_JVM; + + } catch (Exception e) { + LOGGER.warn( + "Could not determine JVM runtime info for {}, assuming {}", + jvmExecutablePath, + JvmInfo.CURRENT_JVM, + e); + return JvmInfo.CURRENT_JVM; + } + } + + private static final class JvmVersionOutputParser + implements ShellCommandExecutor.OutputParser { + @Override + public JvmInfo parse(InputStream inputStream) throws IOException { + String name = null; + String version = null; + String vendor = null; + + BufferedReader bis = + new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset())); + String line; + while ((line = bis.readLine()) != null) { + if (line.contains("java.runtime.name ")) { + name = getPropertyValue(line); + } else if (line.contains("java.version ")) { + version = getPropertyValue(line); + } else if (line.contains("java.vendor ")) { + vendor = getPropertyValue(line); + } + } + return new JvmInfo(name, version, vendor); + } + + private String getPropertyValue(String line) { + // format of the input is: " property.name = propertyValue" + return line.substring(line.indexOf('=') + 2); + } + } +} diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/JvmInfoFactoryTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/JvmInfoFactoryTest.groovy index db6a991d2df..1d438fc2780 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/JvmInfoFactoryTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/config/JvmInfoFactoryTest.groovy @@ -15,7 +15,7 @@ class JvmInfoFactoryTest extends Specification { def currentJvmExecutable = getCurrentJvmExecutable() when: - def jvmInfo = JvmInfoFactory.doGetJvmInfo(currentJvmExecutable) + def jvmInfo = JvmInfoFactoryImpl.doGetJvmInfo(currentJvmExecutable) then: jvmInfo == JvmInfo.CURRENT_JVM diff --git a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityTest.groovy b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityTest.groovy index cd818861261..9c09587944c 100644 --- a/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/testFixtures/groovy/datadog/trace/civisibility/CiVisibilityTest.groovy @@ -12,7 +12,7 @@ import datadog.trace.api.config.CiVisibilityConfig import datadog.trace.api.config.GeneralConfig import datadog.trace.bootstrap.instrumentation.api.Tags import datadog.trace.civisibility.codeowners.Codeowners -import datadog.trace.civisibility.config.JvmInfoFactory +import datadog.trace.civisibility.config.JvmInfoFactoryImpl import datadog.trace.civisibility.config.ModuleExecutionSettingsFactory import datadog.trace.civisibility.coverage.NoopCoverageProbeStore import datadog.trace.civisibility.decorator.TestDecorator @@ -122,7 +122,7 @@ abstract class CiVisibilityTest extends AgentTestRunner { } InstrumentationBridge.registerBuildEventsHandlerFactory { - decorator -> new BuildEventsHandlerImpl<>(buildSystemSessionFactory, new JvmInfoFactory()) + decorator -> new BuildEventsHandlerImpl<>(buildSystemSessionFactory, new JvmInfoFactoryImpl()) } InstrumentationBridge.registerCoverageProbeStoreRegistry(coverageProbeStoreFactory) diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java index 1a3d861c3ac..491ebad369c 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/CiVisibilityConfig.java @@ -58,6 +58,7 @@ public final class CiVisibilityConfig { "civisibility.repo.index.sharing.enabled"; public static final String CIVISIBILITY_MODULE_EXECUTION_SETTINGS_CACHE_SIZE = "civisibility.module.execution.settings.cache.size"; + public static final String CIVISIBILITY_JVM_INFO_CACHE_SIZE = "civisibility.jvm.info.cache.size"; public static final String CIVISIBILITY_COVERAGE_SEGMENTS_ENABLED = "civisibility.coverage.segments.enabled"; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index a322a4c8f1d..8a120408aff 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -160,6 +160,7 @@ import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_JACOCO_PLUGIN_EXCLUDES; import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_JACOCO_PLUGIN_INCLUDES; import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_JACOCO_PLUGIN_VERSION; +import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_JVM_INFO_CACHE_SIZE; import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_MODULE_EXECUTION_SETTINGS_CACHE_SIZE; import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_MODULE_ID; import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_REPO_INDEX_SHARING_ENABLED; @@ -715,6 +716,7 @@ static class HostNameHolder { private final boolean ciVisibilityCiProviderIntegrationEnabled; private final boolean ciVisibilityRepoIndexSharingEnabled; private final int ciVisibilityModuleExecutionSettingsCacheSize; + private final int ciVisibilityJvmInfoCacheSize; private final boolean ciVisibilityCoverageSegmentsEnabled; private final boolean remoteConfigEnabled; @@ -1649,6 +1651,7 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins configProvider.getBoolean(CIVISIBILITY_REPO_INDEX_SHARING_ENABLED, true); ciVisibilityModuleExecutionSettingsCacheSize = configProvider.getInteger(CIVISIBILITY_MODULE_EXECUTION_SETTINGS_CACHE_SIZE, 16); + ciVisibilityJvmInfoCacheSize = configProvider.getInteger(CIVISIBILITY_JVM_INFO_CACHE_SIZE, 8); ciVisibilityCoverageSegmentsEnabled = configProvider.getBoolean(CIVISIBILITY_COVERAGE_SEGMENTS_ENABLED, false); @@ -2729,6 +2732,10 @@ public int getCiVisibilityModuleExecutionSettingsCacheSize() { return ciVisibilityModuleExecutionSettingsCacheSize; } + public int getCiVisibilityJvmInfoCacheSize() { + return ciVisibilityJvmInfoCacheSize; + } + public boolean isCiVisibilityCoverageSegmentsEnabled() { return ciVisibilityCoverageSegmentsEnabled; }