Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add caching to JVM info factory #6162

Merged
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
5 changes: 3 additions & 2 deletions dd-java-agent/agent-ci-visibility/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 <U> BuildEventsHandler<U> create() {
return new BuildEventsHandlerImpl<>(sessionFactory, new JvmInfoFactory());
return new BuildEventsHandlerImpl<>(sessionFactory, jvmInfoFactory);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Path, JvmInfo> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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<JvmInfo> {
@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);
}
Original file line number Diff line number Diff line change
@@ -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<JvmInfo> {
@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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down
7 changes: 7 additions & 0 deletions internal-api/src/main/java/datadog/trace/api/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -2729,6 +2732,10 @@ public int getCiVisibilityModuleExecutionSettingsCacheSize() {
return ciVisibilityModuleExecutionSettingsCacheSize;
}

public int getCiVisibilityJvmInfoCacheSize() {
return ciVisibilityJvmInfoCacheSize;
}

public boolean isCiVisibilityCoverageSegmentsEnabled() {
return ciVisibilityCoverageSegmentsEnabled;
}
Expand Down
Loading