Add continuous profiling and update telemetry for Illuminate - #4807
Add continuous profiling and update telemetry for Illuminate#4807johnoliver wants to merge 11 commits into
Conversation
The DiagnosticsTest ContinuousProfilingJava11Test references applicationinsights-continuous.json via @useagent, but the resource was never committed. On CI the file is absent, so the continuous profiling config fails to load and all three DiagnosticsTest cases fail (the missing config also leaves the shared test setup in a state that breaks the subsequently-run Java11Test.getJfr). Committing the resource fixes the Diagnostics:DiagnosticsTest build failure. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously a profile request against a continuous (circular-buffer) recording always dumped the full local continuousProfilingMaxAge window, silently ignoring the portal-/JMX-configured profile duration carried on the alert breach. captureContinuousRecording now resolves the capture window as min(requestedDuration, maxAge): when the requested duration is shorter than the buffer it streams out only the trailing requested window via Recording.getStream(start, end); when it covers (or exceeds) the buffer it keeps using the more robust dump() path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A breach processed during startup, before startContinuousDiagnostics has run, would schedule a diagnostic-cycle shutdown. Once continuous profiling started and registered the continuous emitters, that stale scheduled shutdown fired and permanently tore them down. Guard the scheduled teardown under a lifecycle lock that checks whether continuous diagnostics has since been enabled, and synchronize the start/stop transitions so the check-and-stop cannot interleave with a concurrent startContinuousDiagnostics. Refactor the JFR-touching helpers into overridable instance methods so the race can be covered by a deterministic unit test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
NoOpProcessDumper.thisProcess() returns null on operating systems that do not support diagnostics. The continuous profiling refactor dropped the guard around the process dumper result, so getThisProcess() would NPE on thisProcess.getPid(). Restore a guard that returns null when no process is available, matching the previous defensive behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses several review findings on the continuous profiling dump path: - Timestamp the uploaded profile (and encode the file name) at the start of the captured window (now - captureWindow) instead of at dump time, so the profile lands at the correct point on the portal timeline (B4). - Keep the dumped file in a local variable and advance the global cooldown inside the recording lock, so concurrent triggers cannot publish or delete each other's files via the shared activeRecordingFile field (B7). - Use a dedicated RecordingOptions.Builder for the continuous recording so the maxAge/disk options no longer permanently mutate the builder shared with on-demand recordings (S4). - Clamp a non-positive continuousProfilingMaxAgeSeconds to the default rather than passing '0 ms'/'-1000 ms' straight to JFR (C4). - Reuse the CPU recording configuration for the continuous recording instead of opening a second stream on the same .jfc resource (B2, partial). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Log the failure when emitting continuous diagnostic breach information, since the returned future is discarded by the caller and the error would otherwise be swallowed (S3). - Drop the redundant inner init() guard in SystemStatsProvider that keyed off a singleton it populates itself; init() is already single-shot via the initialised flag (R5). - Close the Files.walk stream via try-with-resources and select the most recently modified .jfr file in the Diagnostics smoke-test controller, so continuous profiling's extra dumps cannot cause a stale-file false green; extract the shared polling loop (S5). - Document enableContinuousProfiling / continuousProfilingMaxAgeSeconds in docs/README.md, including their limitations, and add CHANGELOG entries for the feature and the MachineStats -> MachineInfo event rename (D1, D2). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The continuous circular buffer is always in the RECORDING state, but the
attempt to honor a shorter requested profile duration streamed a sub-window
via Recording.getStream(start, end), which the JFR connection only permits on
a STOPPED recording. Every manual/portal/JMX trigger therefore threw
IllegalStateException ("Recording state RECORDING not in [STOPPED]"), so no
continuous profile was ever produced and the DiagnosticsTest smoke tests
failed. A live recording can only be dumped in its entirety, so always use
Recording.dump() and drop the infeasible sub-window streaming path. The
uploaded profile is still timestamped at the start of the captured window
(recordingEnd - maxAge). Update the README limitation note accordingly.
Also harden the Diagnostics smoke test controller: continuous profiling can
produce several dumps and a dump may be observed mid-write, so iterate every
candidate .jfr file (newest first) and skip any that cannot be decompressed
yet instead of letting an EOFException fail the whole request.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This pull request updates the Java agent’s profiling/diagnostics pipeline to support continuous profiling (always-on circular-buffer JFR recording) and adjusts diagnostics emission to ensure required diagnostic events are present in continuous snapshots. It also removes the legacy calibration/context-switching subsystem and renames the machine diagnostic event from MachineStats to MachineInfo.
Changes:
- Add continuous profiling configuration and implementation (circular-buffer JFR recording dumped on demand), plus docs/changelog updates.
- Add “continuous diagnostics” lifecycle hooks and update diagnostics emission to support continuous profiling snapshots.
- Remove calibration/context-switching measurement and refactor
MachineStats→MachineInfoacross code and JFC templates.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| smoke-tests/apps/Diagnostics/src/smokeTest/resources/applicationinsights-continuous.json | Adds smoke-test config enabling continuous profiling + diagnostics. |
| smoke-tests/apps/Diagnostics/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/DiagnosticsTest.java | Adds smoke test validating continuous recordings include required diagnostics. |
| smoke-tests/apps/Diagnostics/src/main/java/com/microsoft/applicationinsights/smoketestapp/TestController.java | Adds endpoint + polling logic to validate diagnostic events exist in continuous dumps. |
| smoke-tests/apps/Diagnostics/JfrFileReader/src/main/java/com/microsoft/applicationinsights/jfrfile/JfrFileReader.java | Adds helper to confirm at least one instance of an event type exists in a recording. |
| docs/README.md | Documents continuous profiling settings and limitations. |
| CHANGELOG.md | Notes continuous profiling enhancement and MachineStats → MachineInfo breaking change. |
| agent/agent-tooling/src/test/java/com/microsoft/applicationinsights/agent/internal/profiler/ProfilerContinuousProfilingTest.java | Adds unit tests for continuous profiling dump/upload behavior. |
| agent/agent-tooling/src/main/resources/com/microsoft/applicationinsights/agent/internal/profiler/reduced-memory-profile.jfc | Renames enabled diagnostics event to MachineInfo. |
| agent/agent-tooling/src/main/resources/com/microsoft/applicationinsights/agent/internal/profiler/reduced-cpu-profile.jfc | Renames enabled diagnostics event to MachineInfo. |
| agent/agent-tooling/src/main/resources/com/microsoft/applicationinsights/agent/internal/profiler/diagnostic-memory-profile.jfc | Renames enabled diagnostics event to MachineInfo. |
| agent/agent-tooling/src/main/resources/com/microsoft/applicationinsights/agent/internal/profiler/diagnostic-cpu-profile.jfc | Renames enabled diagnostics event to MachineInfo. |
| agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/triggers/AlertingSubsystemInit.java | Adjusts when diagnostics are emitted relative to profiling start for continuous vs traditional mode. |
| agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/Profiler.java | Implements continuous circular-buffer recording creation + snapshot dump/upload path. |
| agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/PerformanceMonitoringService.java | Starts continuous diagnostics when continuous profiling is enabled. |
| agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java | Adds continuous profiling configuration fields. |
| agent/agent-profiler/agent-diagnostics/src/test/java/com/microsoft/applicationinsights/diagnostics/appinsights/CodeOptimizerDiagnosticEngineJfrTest.java | Adds lifecycle tests for continuous diagnostics vs per-breach cycles. |
| agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/jfr/SystemStatsProvider.java | Removes calibration usage and refactors machine stats to MachineInfo. |
| agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/calibration/ContextSwitchingRunner.java | Removes legacy calibration/context-switching runner. |
| agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/calibration/CalibratorDefault.java | Removes legacy calibrator implementation. |
| agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/calibration/Calibrator.java | Removes legacy calibrator interface. |
| agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/collection/calibration/Calibration.java | Removes legacy calibration model. |
| agent/agent-profiler/agent-diagnostics/src/main/java/com/microsoft/applicationinsights/diagnostics/appinsights/CodeOptimizerDiagnosticEngineJfr.java | Adds continuous diagnostics lifecycle + thread-safety refinements; switches to MachineInfo. |
| agent/agent-profiler/agent-diagnostics/build.gradle.kts | Adds test dependencies for new diagnostics tests. |
| agent/agent-profiler/agent-diagnostics-jfr/src/main/java/com/microsoft/applicationinsights/diagnostics/jfr/MachineInfo.java | Renames event + introduces schema versioning for payload evolution. |
| agent/agent-profiler/agent-diagnostics-api/src/main/java/com/microsoft/applicationinsights/diagnostics/DiagnosticEngine.java | Adds default startContinuousDiagnostics/stopContinuousDiagnostics hooks. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (2)
agent/agent-profiler/agent-diagnostics-jfr/src/main/java/com/microsoft/applicationinsights/diagnostics/jfr/MachineInfo.java:47
MachineInfoJavadoc says recordings produced beforeschemaVersionexisted should be treated as schemaVersion 1 (implicit legacy version), butschemaVersioncurrently defaults to 0 when the field is absent during deserialization. This makes the in-memory representation disagree with the documented schema semantics for older recordings.
agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/profiler/Profiler.java:122continuousProfilingMaxAgeSecondsis validated (and can log a warning) even whenenableContinuousProfilingis false. SincecontinuousProfilingMaxAgeis unused unless continuous profiling is enabled, this can create noisy startup logs for configurations that setcontinuousProfilingMaxAgeSeconds <= 0but keep the feature disabled.
continuousProfilingEnabled = config.enableContinuousProfiling;
continuousProfilingMaxAge =
resolveContinuousProfilingMaxAge(config.continuousProfilingMaxAgeSeconds);
// Continuous profiling uses a single always-on recording, so it can only carry one JFC. Reuse
// the CPU configuration rather than opening a second stream on the same resource.
continuousRecordingConfiguration = cpuRecordingConfiguration;
…quirements
This pull request introduces several important changes related to the diagnostics and profiling system, primarily focused on removing the calibration logic and context switching measurement, and improving support for continuous diagnostics. The changes simplify the codebase by removing unused calibration components, refactoring machine stats to
MachineInfo, and adding mechanisms for continuous diagnostics collection.Diagnostics and Profiling Improvements:
startContinuousDiagnosticsandstopContinuousDiagnosticsmethods to theDiagnosticEngineinterface and implemented them inCodeOptimizerDiagnosticEngineJfr, enabling continuous diagnostics collection for use with continuous profiling. [1] [2]CodeOptimizerDiagnosticEngineJfrto useAtomicIntegerandAtomicBooleanfor thread-safe management of process ID and continuous mode state. [1] [2]MachineInfoinstead of the removed calibration-basedMachineStats; emission now usesemitMachineInfo()and omits context switching rate. [1] [2]Removal of Calibration and Context Switching Logic:
Calibration,Calibrator,CalibratorDefault, andContextSwitchingRunnerclasses, as well as all related usages. [1] [2] [3] [4] [5] [6] [7]MachineInfo; the field remains for backward compatibility but is always zero in new recordings. [1] [2] [3] [4]Refactoring and Renaming:
MachineStatstoMachineInfothroughout the codebase to better reflect its purpose and removed calibration-related logic. [1] [2] [3] [4] [5] [6] [7]These changes streamline the diagnostics system by removing unused complexity and improving support for continuous profiling scenarios.
Fix # .
For significant contributions please make sure you have completed the following items: