Commit 8abc6c3
Fix JFR startup deadlock without disabling built-in JFR events (#11957)
Actually initialize JFR Handlers class early to avoid deadlock
PR #10096 attempted to preload jdk.jfr.events.Handlers via
ClassLoader.loadClass() to prevent an AB-BA deadlock between the JFR
Utils monitor and the Handlers class-initialization lock (JDK-8371889).
However, ClassLoader.loadClass() only loads the class; it does not run
the static initializer and does not acquire the class-initialization
lock. Initialization is still deferred to first active use, inside the
racing window, so the deadlock is not actually prevented.
Use Class.forName(name, true, loader) instead, which forces <clinit> to
run on this thread before the JFR code acquires the Utils monitor. Also
widen the catch to Throwable, since forcing initialization can surface
Errors (ExceptionInInitializerError, NoClassDefFoundError, LinkageError)
that the previous catch (Exception) would not have handled.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address review: cover renamed holder class and init before all JFR registration
Two follow-ups from PR review:
- The JFR event-holder class was renamed across JDK versions. It is
jdk.jfr.events.Handlers only on JDK 17-18; on JDK 19-22 it is
jdk.jfr.events.EventConfigurations (same Utils-based eager init, same
deadlock); on JDK 23+ the pattern was removed. Targeting only Handlers
meant the workaround silently no-op'd (ClassNotFoundException) on JDK
19-22, which are affected. Try both class names.
- registerDeadlockDetectionEvent() also registers a JFR event and runs
before registerSmapEntryEvent(). Hoist the holder-class initialization
into startJmx() ahead of both registrations so it is always fully
initialized before any JFR registration acquires the Utils monitor.
Extracted the logic into initializeJfrEventHolderClass().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Register JDK JFR events before forcing holder init, to avoid null handlers
Forcing the holder class's <clinit> (jdk.jfr.events.Handlers on JDK 17-18,
EventConfigurations on 19-22) before the JDK's built-in events are
registered caches null into its static-final handler fields permanently,
silently disabling the built-in socket/file/exception JFR events.
Empirically verified on JDK 17.0.17 and 21.0.9 (7/7 fields null when the
holder is initialized before FlightRecorder; 0/7 when after).
In the default profiling.start-force-first=false config the profiler
defers FlightRecorder initialization until after startJmx(), so the
previous placement would poison the handlers. Force FlightRecorder init
(which runs JDKEvents.initialize()) before the holder <clinit>, mirroring
the JDK's own JDK-8371889 fix which initializes the holder only after
JDKEvents.initialize(). If FlightRecorder init fails there is nothing to
protect, so the holder init is skipped. Collapsed the handling into a
single catch(Throwable) with a narrow inner ClassNotFoundException for the
Handlers -> EventConfigurations version fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add regression test for JFR handler null-poisoning
Adds JfrEventHolderInitForkedTest, which runs the production
initialization path (Agent.initializeJfrEventHolderClass) in a fresh JVM
and asserts the JDK's built-in JFR event-holder fields are not poisoned to
null. This guards the ordering invariant that FlightRecorder must be
initialized (registering the built-in events) before the holder class's
<clinit> runs; without it the static-final handler fields cache null
permanently and silently disable socket/file/exception JFR events.
Verified: passes on JDK 17 and 21 with the correct ordering, and fails
when the ordering is reversed. Auto-skips on JDKs without the holder class
(JDK 8 and 23+). Behavior underpinning the test was reproduced standalone
on JDK 17.0.17 and 21.0.9 (7/7 fields null when initialized too early,
0/7 when after FlightRecorder).
To make the logic testable, initializeJfrEventHolderClass() takes the
ClassLoader as an argument (the production no-arg overload passes
AGENT_CLASSLOADER). build.gradle adds --add-opens
jdk.jfr/jdk.jfr.events=ALL-UNNAMED so the test can read the holder fields
reflectively.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Route JFR event registration through a single init-first choke point
Both registerDeadlockDetectionEvent() and registerSmapEntryEvent() shared
the same reflective "invoke Factory.registerEvents()" pattern. Extract it
into registerJfrEvents(), which force-initializes the JDK JFR event-holder
class before registering anything. Routing all startup JFR registration
through this one method enforces the ordering invariant structurally, so a
future event registration cannot reintroduce the JDK-8371889 deadlock by
running before the holder is initialized. The explicit pre-init call in
startJmx() is now redundant and removed.
Also unifies exception handling: NoClassDefFoundError/ClassNotFoundException/
UnsupportedClassVersionError -> "not supported" (debug); any other Throwable
-> error. This is slightly more robust for the smap path, which previously
caught only Exception and would have let an Error (e.g. NoClassDefFoundError)
propagate out of startup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge branch 'master' into rkennke/fix-jfr-handlers-clinit-deadlock
Remove duplicated comment in initializeJfrEventHolderClass
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address pre-review: docs, version accuracy, log wording, test caveats
- Correct the Handlers version range to JDK 15-18 (it is absent on 11-14),
and note that JDK 11 LTS and earlier predate the holder class, so they
are unaffected.
- Move the explanatory Javadoc onto the initializeJfrEventHolderClass
implementation (it was on the no-arg wrapper, while @link targets and the
real logic are on the ClassLoader overload).
- Reference the SCP-1278 deadlock mechanism (smap registration vs. a
concurrent JMXFetch ByteBuddy transform) and note the event registration
would trigger FlightRecorder init anyway; we only order it first.
- Capitalize the smap log description so "Smap entry scraping not supported"
reads correctly.
- Document the regression test's scope: it checks the ordering invariant
(non-null handlers), not end-to-end event emission, and assumes a fresh
forked JVM that has not started JFR.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Select JFR holder class by version instead of catching CNFE
Per review, replace the exception-driven "try Handlers, catch
ClassNotFoundException, try EventConfigurations" control flow with an
explicit version check. New jfrEventHolderClassName() maps the running JDK
to its holder class (Handlers on 15-18, EventConfigurations on 19-22, null
otherwise) using JavaVirtualMachine.isJavaVersionAtLeast(), and is the
single source of truth shared by the production init path and the
regression test (which now uses it to decide the target class and skip
JDKs without a holder). The remaining try/catch(Throwable) is genuine
error handling (JFR disabled / init failure), not control flow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge branch 'master' into rkennke/fix-jfr-handlers-clinit-deadlock
Merge branch 'master' into rkennke/fix-jfr-handlers-clinit-deadlock
Scope the JFR holder-class workaround to HotSpot
The build pipeline's test_base:[semeru17] job failed: on JDK 17 the
version check selected jdk.jfr.events.Handlers, but Eclipse OpenJ9 / IBM
Semeru ships a different JFR implementation where the holder / Utils
handler mechanism does not behave like HotSpot, so the regression test's
non-null-handler assertion did not hold. (semeru8/semeru11 passed because
those versions have no holder and the test skips.)
JDK-8371889 is a HotSpot JFR bug, so gate jfrEventHolderClassName() on
JavaVirtualMachine.isHotspot(): it now returns null on non-HotSpot VMs,
making the production init a no-op there and the regression test skip.
Verified the test still runs (not skipped) and passes on HotSpot JDK 17.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>1 parent 084b01b commit 8abc6c3
3 files changed
Lines changed: 218 additions & 26 deletions
File tree
- dd-java-agent/agent-bootstrap
- src
- main/java/datadog/trace/bootstrap
- test/java/datadog/trace/bootstrap
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
75 | 80 | | |
Lines changed: 132 additions & 26 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
908 | 909 | | |
909 | 910 | | |
910 | 911 | | |
| 912 | + | |
| 913 | + | |
911 | 914 | | |
912 | 915 | | |
913 | 916 | | |
| |||
937 | 940 | | |
938 | 941 | | |
939 | 942 | | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
| 962 | + | |
| 963 | + | |
| 964 | + | |
| 965 | + | |
| 966 | + | |
| 967 | + | |
| 968 | + | |
| 969 | + | |
| 970 | + | |
| 971 | + | |
| 972 | + | |
| 973 | + | |
| 974 | + | |
| 975 | + | |
| 976 | + | |
| 977 | + | |
| 978 | + | |
| 979 | + | |
| 980 | + | |
| 981 | + | |
| 982 | + | |
| 983 | + | |
| 984 | + | |
| 985 | + | |
| 986 | + | |
| 987 | + | |
| 988 | + | |
| 989 | + | |
| 990 | + | |
| 991 | + | |
| 992 | + | |
| 993 | + | |
| 994 | + | |
| 995 | + | |
| 996 | + | |
| 997 | + | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
| 1001 | + | |
940 | 1002 | | |
941 | | - | |
942 | | - | |
943 | | - | |
944 | | - | |
945 | | - | |
946 | | - | |
947 | | - | |
948 | | - | |
949 | | - | |
950 | | - | |
951 | | - | |
| 1003 | + | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
| 1014 | + | |
| 1015 | + | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
| 1023 | + | |
| 1024 | + | |
| 1025 | + | |
| 1026 | + | |
| 1027 | + | |
| 1028 | + | |
| 1029 | + | |
| 1030 | + | |
| 1031 | + | |
| 1032 | + | |
| 1033 | + | |
| 1034 | + | |
| 1035 | + | |
952 | 1036 | | |
| 1037 | + | |
| 1038 | + | |
| 1039 | + | |
| 1040 | + | |
| 1041 | + | |
| 1042 | + | |
| 1043 | + | |
953 | 1044 | | |
954 | 1045 | | |
955 | 1046 | | |
956 | 1047 | | |
| 1048 | + | |
| 1049 | + | |
| 1050 | + | |
957 | 1051 | | |
958 | | - | |
959 | | - | |
960 | | - | |
961 | | - | |
962 | | - | |
963 | | - | |
964 | | - | |
965 | | - | |
| 1052 | + | |
| 1053 | + | |
| 1054 | + | |
| 1055 | + | |
| 1056 | + | |
| 1057 | + | |
| 1058 | + | |
| 1059 | + | |
| 1060 | + | |
| 1061 | + | |
| 1062 | + | |
| 1063 | + | |
| 1064 | + | |
| 1065 | + | |
| 1066 | + | |
| 1067 | + | |
| 1068 | + | |
| 1069 | + | |
| 1070 | + | |
966 | 1071 | | |
967 | | - | |
968 | | - | |
969 | | - | |
970 | | - | |
971 | | - | |
972 | | - | |
973 | | - | |
| 1072 | + | |
| 1073 | + | |
| 1074 | + | |
| 1075 | + | |
| 1076 | + | |
| 1077 | + | |
| 1078 | + | |
| 1079 | + | |
974 | 1080 | | |
975 | 1081 | | |
976 | 1082 | | |
| |||
Lines changed: 81 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
0 commit comments