diff --git a/agent/agent-profiler/agent-diagnostics-api/src/main/java/com/microsoft/applicationinsights/diagnostics/DiagnosticEngineFactory.java b/agent/agent-profiler/agent-diagnostics-api/src/main/java/com/microsoft/applicationinsights/diagnostics/DiagnosticEngineFactory.java index 22468d866c7..cd9402ab2a0 100644 --- a/agent/agent-profiler/agent-diagnostics-api/src/main/java/com/microsoft/applicationinsights/diagnostics/DiagnosticEngineFactory.java +++ b/agent/agent-profiler/agent-diagnostics-api/src/main/java/com/microsoft/applicationinsights/diagnostics/DiagnosticEngineFactory.java @@ -4,6 +4,7 @@ package com.microsoft.applicationinsights.diagnostics; import java.util.concurrent.ScheduledExecutorService; +import javax.annotation.Nullable; /** * Factory to be invoked to create a DiagnosticEngine. This factory will be service loaded by the @@ -11,5 +12,6 @@ * this interface. */ public interface DiagnosticEngineFactory { - DiagnosticEngine create(ScheduledExecutorService executorService); + DiagnosticEngine create( + ScheduledExecutorService executorService, @Nullable String cgroupBasePath); } diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/appinsights/CodeOptimizerApplicationInsightFactoryJfr.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/appinsights/CodeOptimizerApplicationInsightFactoryJfr.java index ac7533a2703..b237b9ef862 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/appinsights/CodeOptimizerApplicationInsightFactoryJfr.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/appinsights/CodeOptimizerApplicationInsightFactoryJfr.java @@ -6,13 +6,58 @@ import com.google.auto.service.AutoService; import com.microsoft.applicationinsights.diagnostics.DiagnosticEngine; import com.microsoft.applicationinsights.diagnostics.DiagnosticEngineFactory; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.concurrent.ScheduledExecutorService; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** Factory for Code Optimizer diagnostics to be service loaded */ @AutoService(DiagnosticEngineFactory.class) public class CodeOptimizerApplicationInsightFactoryJfr implements DiagnosticEngineFactory { + + private static final Path FILE_SYSTEM_ROOT = + Paths.get(System.getProperty("applicationinsights.profiler.filesystemRoot", "/")); + private static final Path CGROUP_DIR = Paths.get("./sys/fs/cgroup"); + + private static final Logger logger = + LoggerFactory.getLogger(CodeOptimizerApplicationInsightFactoryJfr.class); + @Override - public DiagnosticEngine create(ScheduledExecutorService executorService) { - return new CodeOptimizerDiagnosticEngineJfr(executorService); + public DiagnosticEngine create( + ScheduledExecutorService executorService, @Nullable String cgroupBasePath) { + Path cgroupPath = getCgroupPath(cgroupBasePath); + return new CodeOptimizerDiagnosticEngineJfr(executorService, cgroupPath); + } + + @SuppressFBWarnings( + value = "SECPTI", // Potential Path Traversal + justification = + "The constructed file path cannot be controlled by an end user of the instrumented application") + @Nullable + private static Path getCgroupPath(@Nullable String cgroupBasePath) { + Path cgroupPath = null; + if (cgroupBasePath != null) { + cgroupPath = Paths.get(cgroupBasePath); + + if (!Files.exists(cgroupPath)) { + logger.warn("Configured Cgroup path {} does not exist, setting to default", cgroupBasePath); + cgroupPath = null; + } + } + + if (cgroupPath == null) { + cgroupPath = FILE_SYSTEM_ROOT.resolve(CGROUP_DIR); + + if (!Files.exists(cgroupPath)) { + logger.warn("Expected default Cgroup path {} does not exist", cgroupBasePath); + cgroupPath = null; + } + } + + return cgroupPath; } } diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/appinsights/CodeOptimizerDiagnosticEngineJfr.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/appinsights/CodeOptimizerDiagnosticEngineJfr.java index 8334272398a..de01d5d6901 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/appinsights/CodeOptimizerDiagnosticEngineJfr.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/appinsights/CodeOptimizerDiagnosticEngineJfr.java @@ -14,6 +14,7 @@ import com.microsoft.applicationinsights.diagnostics.jfr.SystemStatsProvider; import java.io.IOException; import java.io.StringWriter; +import java.nio.file.Path; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; @@ -33,10 +34,13 @@ public class CodeOptimizerDiagnosticEngineJfr implements DiagnosticEngine { public static final long TIME_BEFORE_END_OF_PROFILE_TO_EMIT_EVENT = 10L; private final ScheduledExecutorService executorService; private final Semaphore semaphore = new Semaphore(1, false); + private final Path cgroupBasePath; private int thisPid; - public CodeOptimizerDiagnosticEngineJfr(ScheduledExecutorService executorService) { + public CodeOptimizerDiagnosticEngineJfr( + ScheduledExecutorService executorService, Path cgroupBasePath) { this.executorService = executorService; + this.cgroupBasePath = cgroupBasePath; } @Override @@ -49,14 +53,14 @@ public void init(int thisPid) { this.thisPid = thisPid; logger.debug("Initialising Code Optimizer Diagnostic Engine"); - CodeOptimizerDiagnosticsJfrInit.initFeature(thisPid); + CodeOptimizerDiagnosticsJfrInit.initFeature(thisPid, cgroupBasePath); logger.debug("Code Optimizer Diagnostic Engine Initialised"); } - private static void startDiagnosticCycle(int thisPid) { + private static void startDiagnosticCycle(int thisPid, Path cgroupBasePath) { logger.debug("Starting Code Optimizer Diagnostic Cycle"); - CodeOptimizerDiagnosticsJfrInit.initFeature(thisPid); - CodeOptimizerDiagnosticsJfrInit.start(thisPid); + CodeOptimizerDiagnosticsJfrInit.initFeature(thisPid, cgroupBasePath); + CodeOptimizerDiagnosticsJfrInit.start(thisPid, cgroupBasePath); } private static void endDiagnosticCycle() { @@ -70,13 +74,13 @@ public Future> performDiagnosis(AlertBreach alert) { new CompletableFuture<>(); try { if (semaphore.tryAcquire(SEMAPHORE_TIMEOUT_IN_SEC, TimeUnit.SECONDS)) { - emitInfo(alert); + emitInfo(alert, cgroupBasePath); long profileDurationInSec = alert.getAlertConfiguration().getProfileDurationSeconds(); long end = profileDurationInSec - TIME_BEFORE_END_OF_PROFILE_TO_EMIT_EVENT; - startDiagnosticCycle(thisPid); + startDiagnosticCycle(thisPid, cgroupBasePath); scheduleEmittingAlertBreachEvent(alert, end); @@ -101,7 +105,7 @@ private void scheduleShutdown( executorService.schedule( () -> { try { - emitInfo(alert); + emitInfo(alert, cgroupBasePath); // We do not return a result atm diagnosisResultCompletableFuture.complete(null); @@ -123,7 +127,7 @@ private void scheduleEmittingAlertBreachEvent(AlertBreach alert, long end) { executorService.schedule( () -> { try { - emitInfo(alert); + emitInfo(alert, cgroupBasePath); } catch (RuntimeException e) { logger.error("Failed to emit breach", e); } @@ -132,10 +136,10 @@ private void scheduleEmittingAlertBreachEvent(AlertBreach alert, long end) { TimeUnit.SECONDS); } - private static void emitInfo(AlertBreach alert) { + private static void emitInfo(AlertBreach alert, Path cgroupBasePath) { logger.debug("Emitting Code Optimizer Diagnostic Event"); emitAlertBreachJfrEvent(alert); - CodeOptimizerDiagnosticsJfrInit.emitCGroupData(); + CodeOptimizerDiagnosticsJfrInit.emitCGroupData(cgroupBasePath); emitMachineStats(); } diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/kernel/CGroupDataReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/kernel/CGroupDataReader.java index 212190bb792..5af595f2382 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/kernel/CGroupDataReader.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/kernel/CGroupDataReader.java @@ -19,4 +19,6 @@ public interface CGroupDataReader { long getCpuLimit() throws OperatingSystemInteractionException; long getCpuPeriod() throws OperatingSystemInteractionException; + + boolean isAvailable(); } diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/kernel/CGroupUsageDataReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/kernel/CGroupUsageDataReader.java index f13463ac2a5..3a4217423c6 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/kernel/CGroupUsageDataReader.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/kernel/CGroupUsageDataReader.java @@ -13,4 +13,6 @@ public interface CGroupUsageDataReader extends TwoStepUpdatable, Closeable { @Nullable List getTelemetry(); + + boolean isAvailable(); } diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuSystemReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuSystemReader.java index ecb14207ce6..6ea67dbcf23 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuSystemReader.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuSystemReader.java @@ -3,11 +3,12 @@ package com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.cgroups; -@SuppressWarnings( - "checkstyle:AbbreviationAsWordInName") // CGroup is the standard abbreviation for Control Group +import java.nio.file.Path; + +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") public class CGroupCpuSystemReader extends CGroupValueReader { // total system CPU time (in nanoseconds) consumed by all tasks in this cgroup - public CGroupCpuSystemReader() { - super("/sys/fs/cgroup/cpu,cpuacct/cpuacct.usage_sys"); + public CGroupCpuSystemReader(Path cgroupPath) { + super(cgroupPath.resolve("./cpuacct.usage_sys")); } } diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuUsageReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuUsageReader.java index f642e04872d..55770fd50cb 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuUsageReader.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuUsageReader.java @@ -3,11 +3,12 @@ package com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.cgroups; -@SuppressWarnings( - "checkstyle:AbbreviationAsWordInName") // CGroup is the standard abbreviation for Control Group +import java.nio.file.Path; + +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") public class CGroupCpuUsageReader extends CGroupValueReader { // total CPU usage (in nanoseconds) consumed by all tasks in this cgroup - public CGroupCpuUsageReader() { - super("/sys/fs/cgroup/cpu,cpuacct/cpuacct.usage"); + public CGroupCpuUsageReader(Path cgroupPath) { + super(cgroupPath.resolve("./cpuacct.usage")); } } diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuUserReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuUserReader.java index c797dcdeefe..fd151073313 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuUserReader.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupCpuUserReader.java @@ -3,11 +3,12 @@ package com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.cgroups; -@SuppressWarnings( - "checkstyle:AbbreviationAsWordInName") // CGroup is the standard abbreviation for Control Group +import java.nio.file.Path; + +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") public class CGroupCpuUserReader extends CGroupValueReader { // total user CPU time (in nanoseconds) consumed by all tasks in this cgroup - public CGroupCpuUserReader() { - super("/sys/fs/cgroup/cpu,cpuacct/cpuacct.usage_user"); + public CGroupCpuUserReader(Path cgroupPath) { + super(cgroupPath.resolve("./cpuacct.usage_user")); } } diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupStatReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupStatReader.java index 9bd25f426ba..dfec08eb909 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupStatReader.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupStatReader.java @@ -5,16 +5,15 @@ import com.microsoft.applicationinsights.diagnostics.collection.libos.BigIncrementalCounter; import com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.TwoStepProcReader; -import java.io.File; +import java.nio.file.Path; -@SuppressWarnings( - "checkstyle:AbbreviationAsWordInName") // CGroup is the standard abbreviation for Control Group +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") public class CGroupStatReader extends TwoStepProcReader { private final BigIncrementalCounter user = new BigIncrementalCounter(); private final BigIncrementalCounter system = new BigIncrementalCounter(); - public CGroupStatReader() { - super(new File("/sys/fs/cgroup/cpu,cpuacct/cpuacct.stat"), true); + public CGroupStatReader(Path cgroupPath) { + super(cgroupPath.resolve("./cpuacct.stat").toFile(), true); } @Override diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupValueReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupValueReader.java index 4b076f9b3b6..bb1c23455ad 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupValueReader.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/CGroupValueReader.java @@ -5,15 +5,14 @@ import com.microsoft.applicationinsights.diagnostics.collection.libos.BigIncrementalCounter; import com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.TwoStepProcReader; -import java.io.File; +import java.nio.file.Path; -@SuppressWarnings( - "checkstyle:AbbreviationAsWordInName") // CGroup is the standard abbreviation for Control Group +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") public abstract class CGroupValueReader extends TwoStepProcReader { private final BigIncrementalCounter usage = new BigIncrementalCounter(); - public CGroupValueReader(String fileName) { - super(new File(fileName), true); + public CGroupValueReader(Path file) { + super(file.toFile(), true); } @Override diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/LinuxCGroupDataReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/LinuxCGroupDataReader.java index 0f9782bc13f..dbef2cf80b4 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/LinuxCGroupDataReader.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/LinuxCGroupDataReader.java @@ -5,60 +5,80 @@ import com.microsoft.applicationinsights.diagnostics.collection.libos.OperatingSystemInteractionException; import com.microsoft.applicationinsights.diagnostics.collection.libos.kernel.CGroupDataReader; -import java.io.File; import java.nio.charset.Charset; import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; -@SuppressWarnings( - "checkstyle:AbbreviationAsWordInName") // CGroup is the standard abbreviation for Control Group +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") public class LinuxCGroupDataReader implements CGroupDataReader { - private static final String CGROUP_DIR = "/sys/fs/cgroup"; - private static final String K_MEM_LIMIT_FILE = CGROUP_DIR + "/memory/memory.kmem.limit_in_bytes"; - private static final String MEM_LIMIT_FILE = CGROUP_DIR + "/memory/memory.limit_in_bytes"; - private static final String MEM_SOFT_LIMIT_FILE = - CGROUP_DIR + "/memory/memory.soft_limit_in_bytes"; - private static final String CPU_LIMIT_FILE = CGROUP_DIR + "/cpu,cpuacct/cpu.cfs_quota_us"; - private static final String CPU_PERIOD_FILE = CGROUP_DIR + "/cpu,cpuacct/cpu.cfs_period_us"; + private static final String K_MEM_LIMIT_FILE = "./memory/memory.kmem.limit_in_bytes"; + private static final String MEM_LIMIT_FILE = "./memory/memory.limit_in_bytes"; + private static final String MEM_SOFT_LIMIT_FILE = "./memory/memory.soft_limit_in_bytes"; + private static final String CPU_LIMIT_FILE = "./cpu,cpuacct/cpu.cfs_quota_us"; + private static final String CPU_PERIOD_FILE = "./cpu,cpuacct/cpu.cfs_period_us"; + + private final Path kmemLimitFile; + private final Path memLimitFile; + private final Path memSoftLimitFile; + private final Path cpuLimitFile; + private final Path cpuPeriodFile; + + public LinuxCGroupDataReader(Path cgroupRoot) { + kmemLimitFile = cgroupRoot.resolve(K_MEM_LIMIT_FILE); + memLimitFile = cgroupRoot.resolve(MEM_LIMIT_FILE); + memSoftLimitFile = cgroupRoot.resolve(MEM_SOFT_LIMIT_FILE); + cpuLimitFile = cgroupRoot.resolve(CPU_LIMIT_FILE); + cpuPeriodFile = cgroupRoot.resolve(CPU_PERIOD_FILE); + } @Override public long getKmemLimit() throws OperatingSystemInteractionException { - return readLong(K_MEM_LIMIT_FILE); + return readLong(kmemLimitFile); } @Override public long getMemoryLimit() throws OperatingSystemInteractionException { - return readLong(MEM_LIMIT_FILE); + return readLong(memLimitFile); } @Override public long getMemorySoftLimit() throws OperatingSystemInteractionException { - return readLong(MEM_SOFT_LIMIT_FILE); + return readLong(memSoftLimitFile); } @Override public long getCpuLimit() throws OperatingSystemInteractionException { - return readLong(CPU_LIMIT_FILE); + return readLong(cpuLimitFile); } @Override public long getCpuPeriod() throws OperatingSystemInteractionException { - return readLong(CPU_PERIOD_FILE); + return readLong(cpuPeriodFile); + } + + @Override + public boolean isAvailable() { + return Files.exists(cpuLimitFile) + || Files.exists(memLimitFile) + || Files.exists(memSoftLimitFile) + || Files.exists(kmemLimitFile) + || Files.exists(cpuPeriodFile); } - private static long readLong(String fileName) throws OperatingSystemInteractionException { + private static long readLong(Path file) throws OperatingSystemInteractionException { try { - File file = new File(fileName); - if (!file.exists() || !file.isFile()) { - throw new OperatingSystemInteractionException("File does not exist: " + fileName); + if (!Files.exists(file) || !Files.isRegularFile(file)) { + throw new OperatingSystemInteractionException("File does not exist: " + file.getFileName()); } - List lines = Files.readAllLines(file.toPath(), Charset.defaultCharset()); - if (lines.size() > 0) { + List lines = Files.readAllLines(file, Charset.defaultCharset()); + if (!lines.isEmpty()) { return Long.parseLong(lines.get(0)); } else { - throw new OperatingSystemInteractionException("Unable to read value from: " + fileName); + throw new OperatingSystemInteractionException( + "Unable to read value from: " + file.getFileName()); } } catch (Exception e) { throw new OperatingSystemInteractionException(e); diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/LinuxCGroupUsageDataReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/LinuxCGroupUsageDataReader.java index ae643aac343..fe511144dbd 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/LinuxCGroupUsageDataReader.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroups/LinuxCGroupUsageDataReader.java @@ -5,21 +5,34 @@ import com.microsoft.applicationinsights.diagnostics.collection.libos.kernel.CGroupUsageDataReader; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -@SuppressWarnings( - "checkstyle:AbbreviationAsWordInName") // CGroup is the standard abbreviation for Control Group +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") public class LinuxCGroupUsageDataReader implements CGroupUsageDataReader { + private static final Path CGROUP_CPU_PATH = Paths.get("./cpu,cpuacct/"); - private final CGroupCpuUsageReader cgroupCpuUsageReader = new CGroupCpuUsageReader(); + private final CGroupCpuUsageReader cgroupCpuUsageReader; - private final CGroupCpuUserReader cgroupCpuUserReader = new CGroupCpuUserReader(); + private final CGroupCpuUserReader cgroupCpuUserReader; - private final CGroupCpuSystemReader cgroupCpuSystemReader = new CGroupCpuSystemReader(); + private final CGroupCpuSystemReader cgroupCpuSystemReader; - private final CGroupStatReader cgroupStatReader = new CGroupStatReader(); + private final CGroupStatReader cgroupStatReader; + + private final Path cgroupDirectory; + + public LinuxCGroupUsageDataReader(Path cgroupDirectory) { + this.cgroupDirectory = cgroupDirectory.resolve(CGROUP_CPU_PATH); + cgroupCpuUsageReader = new CGroupCpuUsageReader(this.cgroupDirectory); + cgroupCpuUserReader = new CGroupCpuUserReader(this.cgroupDirectory); + cgroupCpuSystemReader = new CGroupCpuSystemReader(this.cgroupDirectory); + cgroupStatReader = new CGroupStatReader(this.cgroupDirectory); + } @Override public void poll() { @@ -56,6 +69,11 @@ public List getTelemetry() { .collect(Collectors.toList()); } + @Override + public boolean isAvailable() { + return Files.exists(cgroupDirectory); + } + @Override public void close() throws IOException { cgroupCpuUsageReader.close(); diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroupsv2/CGroupv2CpuReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroupsv2/CGroupv2CpuReader.java new file mode 100644 index 00000000000..95163743346 --- /dev/null +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroupsv2/CGroupv2CpuReader.java @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.cgroupsv2; + +import com.microsoft.applicationinsights.diagnostics.collection.libos.BigIncrementalCounter; +import com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.TwoStepProcReader; +import java.nio.file.Path; + +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") +public class CGroupv2CpuReader extends TwoStepProcReader { + + private static final String CPU_USAGE_PROPERTY = "usage_usec"; + private static final String CPU_SYSTEM_PROPERTY = "system_usec"; + private static final String CPU_USER_PROPERTY = "user_usec"; + + private final BigIncrementalCounter cpuUsage = new BigIncrementalCounter(); + private final BigIncrementalCounter cpuSystem = new BigIncrementalCounter(); + private final BigIncrementalCounter cpuUser = new BigIncrementalCounter(); + + // total CPU usage (in microseconds) consumed by all tasks in this cgroup + public CGroupv2CpuReader(Path cgroupDir) { + super(cgroupDir.resolve("./cpu.stat").toFile()); + } + + @Override + protected void parseLine(String line) { + String[] tokens = line.split(" "); + + if (tokens.length == 2) { + if (CPU_USAGE_PROPERTY.equals(tokens[0])) { + cpuUsage.newValue(Long.parseLong(tokens[1])); + } else if (CPU_SYSTEM_PROPERTY.equals(tokens[0])) { + cpuSystem.newValue(Long.parseLong(tokens[1])); + } else if (CPU_USER_PROPERTY.equals(tokens[0])) { + cpuUser.newValue(Long.parseLong(tokens[1])); + } + } + } + + public BigIncrementalCounter getCpuUsage() { + return cpuUsage; + } + + public BigIncrementalCounter getCpuSystem() { + return cpuSystem; + } + + public BigIncrementalCounter getCpuUser() { + return cpuUser; + } +} diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroupsv2/LinuxCGroupV2DataReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroupsv2/LinuxCGroupV2DataReader.java new file mode 100644 index 00000000000..b199ba7c014 --- /dev/null +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroupsv2/LinuxCGroupV2DataReader.java @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.cgroupsv2; + +import com.microsoft.applicationinsights.diagnostics.collection.libos.OperatingSystemInteractionException; +import com.microsoft.applicationinsights.diagnostics.collection.libos.kernel.CGroupDataReader; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") +public class LinuxCGroupV2DataReader implements CGroupDataReader { + private static final Path MEM_MAX_FILE = Paths.get("./memory.max"); + private static final Path MEM_HIGH_FILE = Paths.get("./memory.high"); + private static final Path CPU_MAX_FILE = Paths.get("./cpu.max"); + + private final Path memMaxFile; + private final Path memHighFile; + private final Path cpuMaxFile; + + public LinuxCGroupV2DataReader(Path cgroupDir) { + memMaxFile = cgroupDir.resolve(MEM_MAX_FILE); + memHighFile = cgroupDir.resolve(MEM_HIGH_FILE); + cpuMaxFile = cgroupDir.resolve(CPU_MAX_FILE); + } + + @Override + public long getKmemLimit() { + // In cgroup v2, kernel memory accounting is not separately exposed + // Return a large value to indicate no specific limit + return Long.MAX_VALUE; + } + + @Override + public long getMemoryLimit() { + return parseMemoryValue(memMaxFile); + } + + @Override + public long getMemorySoftLimit() { + return parseMemoryValue(memHighFile); + } + + @Override + public long getCpuLimit() { + return parseCpuQuota(); + } + + @Override + public long getCpuPeriod() { + return parseCpuPeriod(); + } + + @Override + public boolean isAvailable() { + return Files.exists(memMaxFile) || Files.exists(memHighFile) || Files.exists(cpuMaxFile); + } + + private static long parseMemoryValue(Path file) { + try { + String content = readFileContent(file); + if ("max".equalsIgnoreCase(content.trim())) { + return Long.MAX_VALUE; + } + return Long.parseLong(content.trim()); + } catch (Exception e) { + return Long.MAX_VALUE; + } + } + + private long parseCpuQuota() { + try { + String content = readFileContent(cpuMaxFile); + String[] parts = content.trim().split("\\s+"); + if (parts.length >= 1) { + if ("max".equalsIgnoreCase(parts[0])) { + return -1; // No quota defined + } + return Long.parseLong(parts[0]); + } + } catch (Exception ignored) { + return -1; // No quota defined + } + return -1; // No quota defined + } + + private long parseCpuPeriod() { + try { + String content = readFileContent(cpuMaxFile); + String[] parts = content.trim().split("\\s+"); + if (parts.length >= 2) { + return Long.parseLong(parts[1]); + } + } catch (Exception ignored) { + return -1; // No quota defined + } + return -1; // No period defined + } + + private static String readFileContent(Path file) throws OperatingSystemInteractionException { + try { + if (!Files.exists(file) || !Files.isRegularFile(file)) { + throw new OperatingSystemInteractionException( + "File does not exist: " + file.getFileName().toString()); + } + + List lines = Files.readAllLines(file, Charset.defaultCharset()); + if (lines.isEmpty()) { + throw new OperatingSystemInteractionException( + "Empty file: " + file.getFileName().toString()); + } + return lines.get(0); + } catch (Exception e) { + throw new OperatingSystemInteractionException(e); + } + } +} diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroupsv2/LinuxCGroupV2UsageDataReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroupsv2/LinuxCGroupV2UsageDataReader.java new file mode 100644 index 00000000000..73b72b9c73c --- /dev/null +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/linux/cgroupsv2/LinuxCGroupV2UsageDataReader.java @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.cgroupsv2; + +import com.microsoft.applicationinsights.diagnostics.collection.libos.kernel.CGroupUsageDataReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") +public class LinuxCGroupV2UsageDataReader implements CGroupUsageDataReader { + + private final CGroupv2CpuReader cgroupV2CpuReader; + private final Path cgroupDir; + + public LinuxCGroupV2UsageDataReader(Path cgroupDir) { + this.cgroupDir = cgroupDir; + cgroupV2CpuReader = new CGroupv2CpuReader(cgroupDir); + } + + @Override + public void poll() { + cgroupV2CpuReader.poll(); + } + + @Override + public void update() { + cgroupV2CpuReader.update(); + } + + @Override + public List getTelemetry() { + return Stream.of( + cgroupV2CpuReader.getCpuUsage().getIncrement(), + cgroupV2CpuReader.getCpuUser().getIncrement(), + cgroupV2CpuReader.getCpuSystem().getIncrement(), + cgroupV2CpuReader.getCpuUser().getIncrement(), + cgroupV2CpuReader.getCpuSystem().getIncrement()) + .map( + value -> { + if (value == null) { + return -1.0d; + } else { + return value.doubleValue(); + } + }) + .collect(Collectors.toList()); + } + + @Override + public boolean isAvailable() { + return Files.exists(cgroupDir.resolve("./cgroup.controllers")); + } + + @Override + public void close() throws IOException { + cgroupV2CpuReader.close(); + } +} diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/nop/NoOpCGroupDataReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/nop/NoOpCGroupDataReader.java index e0304030e46..97561e0d682 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/nop/NoOpCGroupDataReader.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/nop/NoOpCGroupDataReader.java @@ -35,4 +35,9 @@ public long getCpuLimit() { public long getCpuPeriod() { return -1; } + + @Override + public boolean isAvailable() { + return true; + } } diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/nop/NoOpCGroupUsageDataReader.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/nop/NoOpCGroupUsageDataReader.java index 794971337fe..9b1c90ff0e5 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/nop/NoOpCGroupUsageDataReader.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/libos/os/nop/NoOpCGroupUsageDataReader.java @@ -25,4 +25,9 @@ public void update() {} @Override public void close() throws IOException {} + + @Override + public boolean isAvailable() { + return true; + } } diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/jfr/CodeOptimizerDiagnosticsJfrInit.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/jfr/CodeOptimizerDiagnosticsJfrInit.java index d5007db4b29..55dbd815331 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/jfr/CodeOptimizerDiagnosticsJfrInit.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/jfr/CodeOptimizerDiagnosticsJfrInit.java @@ -7,6 +7,7 @@ import com.microsoft.applicationinsights.diagnostics.collection.libos.OperatingSystemInteractionException; import com.microsoft.applicationinsights.diagnostics.collection.libos.os.OperatingSystemDetector; import java.io.IOException; +import java.nio.file.Path; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -26,8 +27,9 @@ public class CodeOptimizerDiagnosticsJfrInit { private static final AtomicInteger exceptionLogCount = new AtomicInteger(0); private static final AtomicInteger telemetryFailureLogCount = new AtomicInteger(0); - private static final Runnable readCGroupData = CodeOptimizerDiagnosticsJfrInit::emitCGroupData; private static final AtomicReference telemetryEmitter = new AtomicReference<>(null); + private static final AtomicReference cgroupTelemetryEmitter = + new AtomicReference<>(null); private CodeOptimizerDiagnosticsJfrInit() {} @@ -70,9 +72,9 @@ private static void logFailure(String logLine, @Nullable Exception e, AtomicInte @SuppressWarnings( "checkstyle:AbbreviationAsWordInName") // CGroup is the standard abbreviation for Control // Group - public static void emitCGroupData() { + public static void emitCGroupData(Path cgroupBasePath) { try { - CGroupData cgroupData = SystemStatsProvider.getCGroupData(); + CGroupData cgroupData = SystemStatsProvider.getCGroupData(cgroupBasePath); if (cgroupData != null) { cgroupData.commit(); @@ -86,22 +88,23 @@ public static boolean isOsSupported() { return OperatingSystemDetector.getOperatingSystem().supportsDiagnostics(); } - public static void initFeature(int thisPid) { + public static void initFeature(int thisPid, Path cgroupBasePath) { if (!isOsSupported()) { return; } // eagerly get stats to warm it up - SystemStatsProvider.init(thisPid); + SystemStatsProvider.init(thisPid, cgroupBasePath); } - public static void start(int thisPidSupplier) { + public static void start(int thisPidSupplier, Path cgroupBasePath) { if (!isOsSupported()) { return; } if (running.compareAndSet(false, true)) { - SystemStatsReader statsReader = SystemStatsProvider.getStatsReader(thisPidSupplier); + SystemStatsReader statsReader = + SystemStatsProvider.getStatsReader(thisPidSupplier, cgroupBasePath); Runnable emitter = emitTelemetry(statsReader); if (telemetryEmitter.compareAndSet(null, emitter)) { FlightRecorder.addPeriodicEvent(Telemetry.class, emitter); @@ -112,9 +115,12 @@ public static void start(int thisPidSupplier) { logger.error("Failed to init stats reader", e); } } - FlightRecorder.addPeriodicEvent(CGroupData.class, readCGroupData); - readCGroupData.run(); + if (cgroupTelemetryEmitter.compareAndSet(null, () -> emitCGroupData(cgroupBasePath))) { + FlightRecorder.addPeriodicEvent(CGroupData.class, cgroupTelemetryEmitter.get()); + } + + cgroupTelemetryEmitter.get().run(); } } @@ -128,7 +134,11 @@ public static void stop() { FlightRecorder.removePeriodicEvent(telemetryEmitter.get()); telemetryEmitter.set(null); } - FlightRecorder.removePeriodicEvent(readCGroupData); + + if (cgroupTelemetryEmitter.get() != null) { + FlightRecorder.removePeriodicEvent(cgroupTelemetryEmitter.get()); + cgroupTelemetryEmitter.set(null); + } SystemStatsProvider.close(); } } diff --git a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/jfr/SystemStatsProvider.java b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/jfr/SystemStatsProvider.java index 0455ce9dd2c..953ffd970f8 100644 --- a/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/jfr/SystemStatsProvider.java +++ b/agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/jfr/SystemStatsProvider.java @@ -20,6 +20,8 @@ import com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.LinuxProcessDumper; import com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.cgroups.LinuxCGroupDataReader; import com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.cgroups.LinuxCGroupUsageDataReader; +import com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.cgroupsv2.LinuxCGroupV2DataReader; +import com.microsoft.applicationinsights.diagnostics.collection.libos.os.linux.cgroupsv2.LinuxCGroupV2UsageDataReader; import com.microsoft.applicationinsights.diagnostics.collection.libos.os.nop.NoOpCGroupDataReader; import com.microsoft.applicationinsights.diagnostics.collection.libos.os.nop.NoOpCGroupUsageDataReader; import com.microsoft.applicationinsights.diagnostics.collection.libos.os.nop.NoOpKernelMonitor; @@ -30,6 +32,7 @@ import com.microsoft.applicationinsights.diagnostics.collection.libos.process.ThisPidSupplier; import java.io.Closeable; import java.io.IOException; +import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -53,7 +56,7 @@ public class SystemStatsProvider { private SystemStatsProvider() {} - public static void init(int thisPid) { + public static void init(int thisPid, Path cgroupBasePath) { // Ensure we only initialize once if (initialised.compareAndSet(false, true)) { singletons.put(ThisPidSupplier.class, new AtomicReference<>((ThisPidSupplier) () -> thisPid)); @@ -62,7 +65,7 @@ public static void init(int thisPid) { try { getCalibration(); getMachineStats(); - getCGroupData(); + getCGroupData(cgroupBasePath); // Close until needed close(); @@ -108,12 +111,12 @@ private static T getSingleton(Class clazz, Supplier supplier) { "checkstyle:AbbreviationAsWordInName", "MemberName" }) // CGroup is the standard abbreviation for Control Group - public static CGroupData getCGroupData() { + public static CGroupData getCGroupData(Path cgroupBasePath) { return getSingleton( CGroupData.class, () -> { try { - CGroupDataReader reader = buildCGroupDataReader(); + CGroupDataReader reader = buildCGroupDataReader(cgroupBasePath); CGroupData data = new CGroupData(); return data.setKmemLimit(reader.getKmemLimit()) @@ -169,10 +172,21 @@ private static Process getThisProcess() { @SuppressWarnings( "checkstyle:AbbreviationAsWordInName") // CGroup is the standard abbreviation for Control // Group - private static CGroupDataReader buildCGroupDataReader() { + private static CGroupDataReader buildCGroupDataReader(Path cgroupBasePath) { switch (OperatingSystemDetector.getOperatingSystem()) { case LINUX: - return new LinuxCGroupDataReader(); + CGroupDataReader dataReader = new LinuxCGroupDataReader(cgroupBasePath); + if (dataReader.isAvailable()) { + return dataReader; + } + + dataReader = new LinuxCGroupV2DataReader(cgroupBasePath); + if (dataReader.isAvailable()) { + return dataReader; + } + + logger.info("No CGroup limits data not found"); + return new NoOpCGroupDataReader(); default: return new NoOpCGroupDataReader(); } @@ -194,13 +208,29 @@ private static ProcessDumper getProcessDumper() { @SuppressWarnings( "checkstyle:AbbreviationAsWordInName") // CGroup is the standard abbreviation for Control // Group - private static CGroupUsageDataReader buildCGroupUsageDataReader() { + private static CGroupUsageDataReader buildCGroupUsageDataReader(Path cgroupBasePath) { return getSingleton( CGroupUsageDataReader.class, () -> { + if (cgroupBasePath == null) { + logger.info("No CGroup data present"); + return new NoOpCGroupUsageDataReader(); + } + switch (OperatingSystemDetector.getOperatingSystem()) { case LINUX: - return new LinuxCGroupUsageDataReader(); + CGroupUsageDataReader usageReader = new LinuxCGroupUsageDataReader(cgroupBasePath); + if (usageReader.isAvailable()) { + return usageReader; + } + + usageReader = new LinuxCGroupV2UsageDataReader(cgroupBasePath); + if (usageReader.isAvailable()) { + return usageReader; + } + + logger.warn("CGroup data not found"); + return new NoOpCGroupUsageDataReader(); default: return new NoOpCGroupUsageDataReader(); } @@ -220,15 +250,15 @@ private static MemoryInfoReader buildMemoryInfoReader() { }); } - private static SystemStatsReader getSystemStatsReader() { - return getSingleton(SystemStatsReader.class, SystemStatsProvider::buildSystemStatsReader); + private static SystemStatsReader getSystemStatsReader(Path cgroupBasePath) { + return getSingleton(SystemStatsReader.class, () -> buildSystemStatsReader(cgroupBasePath)); } - private static SystemStatsReader buildSystemStatsReader() { + private static SystemStatsReader buildSystemStatsReader(Path cgroupBasePath) { SystemStatsReader ssr = new SystemStatsReader( getKernelMonitor(), - buildCGroupUsageDataReader(), + buildCGroupUsageDataReader(cgroupBasePath), getThisProcess().getCpuStats(), getThisProcess().getIoStats(), buildMemoryInfoReader()); @@ -257,9 +287,9 @@ private static KernelMonitorDeviceDriver getKernelMonitor() { }); } - public static SystemStatsReader getStatsReader(int thisPidSupplier) { - init(thisPidSupplier); - return getSystemStatsReader(); + public static SystemStatsReader getStatsReader(int thisPidSupplier, Path cgroupBasePath) { + init(thisPidSupplier, cgroupBasePath); + return getSystemStatsReader(cgroupBasePath); } public static void close() { diff --git a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java index a39a66b454c..26005a97585 100644 --- a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java +++ b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java @@ -1530,6 +1530,7 @@ public static class ProfilerConfiguration { public boolean enableDiagnostics = false; public boolean enableRequestTriggering = false; public List requestTriggerEndpoints = new ArrayList<>(); + @Nullable public String cgroupPath = null; } public static class GcEventConfiguration { diff --git a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/PerformanceMonitoringService.java b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/PerformanceMonitoringService.java index a49d2f24d5f..8a4860ab594 100644 --- a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/PerformanceMonitoringService.java +++ b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/PerformanceMonitoringService.java @@ -145,7 +145,8 @@ private DiagnosticEngine startDiagnosticEngine() { 1, ThreadPoolUtils.createNamedDaemonThreadFactory("DiagnosisThreadPool")); DiagnosticEngine diagnosticEngine = - diagnosticEngineFactory.create(diagnosticEngineExecutorService); + diagnosticEngineFactory.create( + diagnosticEngineExecutorService, configuration.cgroupPath); if (diagnosticEngine != null) { diagnosticEngine.init(Integer.parseInt(new PidFinder().getValue(System::getenv))); diff --git a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/service/ServiceProfilerClient.java b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/service/ServiceProfilerClient.java index b4388ab2d5b..829a165eaf4 100644 --- a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/service/ServiceProfilerClient.java +++ b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/service/ServiceProfilerClient.java @@ -123,19 +123,23 @@ private static Mono reportUploadFinish(HttpResponse response) { // this shouldn't happen, the mono should complete with a response or a failure return Mono.error(new AssertionError("http response mono returned empty")); } - try { - int statusCode = response.getStatusCode(); - if (statusCode != 201 && statusCode != 202) { - logger.error("Trace upload failed: {}", statusCode); - return Mono.error(new AssertionError("http request failed")); - } - return response.getBodyAsString(); - } finally { - // need to consume the body or close the response, otherwise get netty ByteBuf leak warnings: - // io.netty.util.ResourceLeakDetector - LEAK: ByteBuf.release() was not called before - // it's garbage-collected (see https://github.com/Azure/azure-sdk-for-java/issues/10467) - response.close(); + int statusCode = response.getStatusCode(); + if (statusCode != 201 && statusCode != 202) { + logger.error("Trace upload failed: {}", statusCode); + return Mono.error(new AssertionError("http request failed")); } + + return response + .getBodyAsString() + .doFinally( + done -> { + // need to consume the body or close the response, otherwise get netty ByteBuf leak + // warnings: + // io.netty.util.ResourceLeakDetector - LEAK: ByteBuf.release() was not called before + // it's garbage-collected (see + // https://github.com/Azure/azure-sdk-for-java/issues/10467) + response.close(); + }); } /** Obtain current settings that have been configured within the UI. */ diff --git a/smoke-tests/apps/DiagnosticExtension/MockExtension/src/main/java/com/microsoft/applicationinsights/smoketestextension/MockDiagnosticEngineFactory.java b/smoke-tests/apps/DiagnosticExtension/MockExtension/src/main/java/com/microsoft/applicationinsights/smoketestextension/MockDiagnosticEngineFactory.java index e1114d2fb9e..c278cb61a38 100644 --- a/smoke-tests/apps/DiagnosticExtension/MockExtension/src/main/java/com/microsoft/applicationinsights/smoketestextension/MockDiagnosticEngineFactory.java +++ b/smoke-tests/apps/DiagnosticExtension/MockExtension/src/main/java/com/microsoft/applicationinsights/smoketestextension/MockDiagnosticEngineFactory.java @@ -13,7 +13,7 @@ public class MockDiagnosticEngineFactory implements DiagnosticEngineFactory { @Override - public DiagnosticEngine create(ScheduledExecutorService executorService) { + public DiagnosticEngine create(ScheduledExecutorService executorService, String cgroupBasePath) { return new DiagnosticEngine() { @Override