@@ -749,3 +749,112 @@ Java_com_datadoghq_profiler_JavaProfiler_dumpContext(JNIEnv* env, jclass unused)
749749 ContextApi::get (spanId, rootSpanId);
750750 TEST_LOG (" ===> Context: tid:%lu, spanId=%lu, rootSpanId=%lu" , OS::threadId (), spanId, rootSpanId);
751751}
752+
753+ // PROF-15341: LivenessTracker/ReferenceChainTracker test seams. Unlike
754+ // testlog()/dumpContext() above (harmless no-ops in release, via TEST_LOG's
755+ // own release-mode expansion to nothing), these mutate real tracker state
756+ // (tagging objects, seeding population history) - shipping them into a
757+ // release build would let a caller corrupt the actual leak-detection state,
758+ // not just add a silent no-op. Guarded out entirely instead, so they only
759+ // exist in the debug build ddprof-test's `testdebug` Gradle task loads
760+ // (`-DDEBUG`, see ConfigurationPresets.kt's configureDebug()) - never in the
761+ // `-DNDEBUG` release build.
762+ #ifdef DEBUG
763+ #include " livenessTracker.h"
764+ #include " referenceChains.h"
765+
766+ extern " C" DLLEXPORT jboolean JNICALL
767+ Java_com_datadoghq_profiler_JavaProfiler_setGcGenerationsEnabled0 (
768+ JNIEnv *env, jclass unused, jboolean enabled) {
769+ LivenessTracker::instance ()->setGcGenerationsForTest (enabled);
770+ return JNI_TRUE ;
771+ }
772+
773+ extern " C" DLLEXPORT void JNICALL
774+ Java_com_datadoghq_profiler_JavaProfiler_seedKlassPopulationSample0 (
775+ JNIEnv *env, jclass unused, jint klassId, jint count, jlong epoch) {
776+ int slot;
777+ bool created;
778+ LivenessTracker::instance ()->klassPopulationRecordForTest (
779+ (u32 )klassId, (u16 )count, (u64 )epoch, &slot, &created);
780+ }
781+
782+ // Wires a real, caller-chosen live object in as klassId's leak-candidate
783+ // representative, so a test-seeded slope signal (seedKlassPopulationSample0
784+ // above) and a directly-tagged frontier root (tagAsReferenceChainRoot0
785+ // below) can be joined into one deterministic end-to-end run of
786+ // pollWatchedTargets()'s bridging step - without either LivenessTracker's
787+ // real allocation sampler or ReferenceChainTracker's root-seeded walk ever
788+ // running. Takes its own weak global ref (klassPopulationSetRepresentativeForTest()'s
789+ // own contract, livenessTracker.h) rather than aliasing any handle the
790+ // caller manages.
791+ extern " C" DLLEXPORT void JNICALL
792+ Java_com_datadoghq_profiler_JavaProfiler_setKlassPopulationRepresentativeForTest0 (
793+ JNIEnv *env, jclass unused, jint klassId, jobject representative) {
794+ jweak rep = env->NewWeakGlobalRef (representative);
795+ LivenessTracker::instance ()->klassPopulationSetRepresentativeForTest (
796+ (u32 )klassId, rep);
797+ }
798+
799+ extern " C" DLLEXPORT void JNICALL
800+ Java_com_datadoghq_profiler_JavaProfiler_resetKlassPopulationForTest0 (
801+ JNIEnv *env, jclass unused) {
802+ LivenessTracker::instance ()->klassPopulationResetForTest ();
803+ }
804+
805+ extern " C" DLLEXPORT jintArray JNICALL
806+ Java_com_datadoghq_profiler_JavaProfiler_selectLeakCandidateKlassIds0 (
807+ JNIEnv *env, jclass unused) {
808+ KlassCandidate candidates[5 ];
809+ int n = LivenessTracker::instance ()->selectLeakCandidates (candidates, 5 );
810+ jintArray result = env->NewIntArray (n);
811+ if (result == nullptr || n == 0 ) {
812+ return result;
813+ }
814+ jint ids[5 ];
815+ for (int i = 0 ; i < n; i++) {
816+ ids[i] = (jint)candidates[i].klass_id ;
817+ }
818+ env->SetIntArrayRegion (result, 0 , n, ids);
819+ return result;
820+ }
821+
822+ extern " C" DLLEXPORT jlong JNICALL
823+ Java_com_datadoghq_profiler_JavaProfiler_tagAsReferenceChainRoot0 (
824+ JNIEnv *env, jclass unused, jobject target) {
825+ jvmtiEnv *jvmti = VM::jvmti ();
826+ if (jvmti == nullptr ) {
827+ return 0 ;
828+ }
829+ return ReferenceChainTracker::instance ()->tagAsRootForTest (jvmti, env,
830+ target);
831+ }
832+
833+ extern " C" DLLEXPORT jboolean JNICALL
834+ Java_com_datadoghq_profiler_JavaProfiler_runReferenceChainPass0 (
835+ JNIEnv *env, jclass unused) {
836+ jvmtiEnv *jvmti = VM::jvmti ();
837+ if (jvmti == nullptr ) {
838+ return JNI_FALSE ;
839+ }
840+ return ReferenceChainTracker::instance ()->runPass (jvmti, env);
841+ }
842+
843+ extern " C" DLLEXPORT void JNICALL
844+ Java_com_datadoghq_profiler_JavaProfiler_pollReferenceChainTargets0 (
845+ JNIEnv *env, jclass unused) {
846+ jvmtiEnv *jvmti = VM::jvmti ();
847+ if (jvmti == nullptr ) {
848+ return ;
849+ }
850+ ReferenceChainTracker::instance ()->pollWatchedTargets (jvmti, env);
851+ }
852+
853+ extern " C" DLLEXPORT jint JNICALL
854+ Java_com_datadoghq_profiler_JavaProfiler_drainReferenceChainEventCount0 (
855+ JNIEnv *env, jclass unused) {
856+ std::vector<ReferenceChainEvent> events;
857+ ReferenceChainTracker::instance ()->drainPendingChainEvents (&events);
858+ return (jint)events.size ();
859+ }
860+ #endif // DEBUG
0 commit comments