1 /*
  2  * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 package jdk.jfr.jcmd;
 25 
 26 import java.io.File;
 27 import java.io.IOException;
 28 import java.util.ArrayList;
 29 import java.util.Collections;
 30 import java.util.HashMap;
 31 import java.util.List;
 32 import java.util.Map;
 33 
 34 import jdk.jfr.Enabled;
 35 import jdk.jfr.Recording;
 36 import jdk.jfr.consumer.RecordedEvent;
 37 import jdk.jfr.consumer.RecordedObject;
 38 import jdk.jfr.consumer.RecordingFile;
 39 import jdk.jfr.internal.test.WhiteBox;
 40 import jdk.test.lib.jfr.EventNames;
 41 
 42 /**
 43  * @test
 44  * @requires vm.flagless
 45  * @requires vm.hasJFR
 46  * @requires !(vm.opt.final.UseCompactObjectHeaders == true | vm.opt.final.UseShenandoahGC == true)
 47  * @summary Start a recording with or without path-to-gc-roots
 48  * @modules jdk.jfr/jdk.jfr.internal.test
 49  * @library /test/lib /test/jdk
 50  *
 51  * @run main/othervm -XX:TLABSize=2k jdk.jfr.jcmd.TestJcmdDumpPathToGCRoots
 52  */
 53 public class TestJcmdDumpPathToGCRoots {
 54 
 55     private static final int OBJECT_COUNT = 100_000;
 56     public static List<Object[]> leak = new ArrayList<>(OBJECT_COUNT);
 57 
 58     public static void main(String[] args) throws Exception {
 59         WhiteBox.setWriteAllObjectSamples(true);
 60 
 61         String settingName = EventNames.OldObjectSample + "#" + "cutoff";
 62 
 63         // dump parameter trumps previous setting
 64         testDump("path-to-gc-roots=true", Collections.singletonMap(settingName, "infinity"), true);
 65         testDump("path-to-gc-roots=true", Collections.singletonMap(settingName, "0 ns"), true);
 66         testDump("path-to-gc-roots=true", Collections.emptyMap(), true);
 67 
 68         testDump("path-to-gc-roots=false", Collections.singletonMap(settingName, "infinity"), false);
 69         testDump("path-to-gc-roots=false", Collections.singletonMap(settingName, "0 ns"), false);
 70         testDump("path-to-gc-roots=false", Collections.emptyMap(), false);
 71 
 72         testDump("", Collections.singletonMap(settingName, "infinity"), true);
 73         testDump("", Collections.singletonMap(settingName, "0 ns"), false);
 74         testDump("", Collections.emptyMap(), false);
 75     }
 76 
 77     private static void testDump(String pathToGcRoots, Map<String, String> settings, boolean expectedChains) throws Exception {
 78         while (true) {
 79             try (Recording r = new Recording()) {
 80                 Map<String, String> p = new HashMap<>(settings);
 81                 p.put(EventNames.OldObjectSample + "#" + Enabled.NAME, "true");
 82                 r.setName("dodo");
 83                 r.setSettings(p);
 84                 r.setToDisk(true);
 85                 r.start();
 86                 clearLeak();
 87                 System.out.println("Recording id: " + r.getId());
 88                 System.out.println("Settings: " + settings.toString());
 89                 System.out.println("Command: JFR.dump " + pathToGcRoots);
 90                 System.out.println("Chains expected: " + expectedChains);
 91                 buildLeak();
 92                 System.gc();
 93                 System.gc();
 94                 File recording = new File("TestJcmdDumpPathToGCRoots" + r.getId() + ".jfr");
 95                 recording.delete();
 96                 JcmdHelper.jcmd("JFR.dump", "name=dodo", pathToGcRoots, "filename=" + recording.getAbsolutePath());
 97                 r.setSettings(Collections.emptyMap());
 98                 List<RecordedEvent> events = RecordingFile.readAllEvents(recording.toPath());
 99                 if (events.isEmpty()) {
100                     System.out.println("No events found in recording. Retrying.");
101                     continue;
102                 }
103                 boolean chains = hasChains(events);
104                 if (expectedChains && !chains) {
105                     System.out.println(events);
106                     System.out.println("Expected chains but found none. Retrying.");
107                     continue;
108                 }
109                 if (!expectedChains && chains) {
110                     System.out.println(events);
111                     System.out.println("Didn't expect chains but found some. Retrying.");
112                     continue;
113                 }
114                 return; // Success
115             }
116         }
117     }
118 
119     private static void clearLeak() {
120       leak.clear();
121       System.gc();
122     }
123 
124     private static boolean hasChains(List<RecordedEvent> events) throws IOException {
125         for (RecordedEvent e : events) {
126             RecordedObject ro = e.getValue("object");
127             if (ro.getValue("referrer") != null) {
128                 return true;
129             }
130         }
131         return false;
132     }
133 
134     private static void buildLeak() {
135         for (int i = 0; i < OBJECT_COUNT;i ++) {
136             leak.add(new Object[0]);
137         }
138     }
139 }