1 /*
  2  * Copyright (c) 2023, 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 
 25 /*
 26  * @test
 27  * @summary test the handling of classes that are excluded from the CDS dump.
 28  * @requires vm.cds.write.archived.java.heap
 29  * @library /test/jdk/lib/testlibrary /test/lib
 30  *          /test/hotspot/jtreg/runtime/cds/appcds/leyden/test-classes
 31  * @build ExcludedClasses Custy
 32  * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
 33  *                 TestApp
 34  *                 TestApp$Foo
 35  *                 TestApp$Foo$Bar
 36  *                 TestApp$Foo$ShouldBeExcluded
 37  *                 TestApp$MyInvocationHandler
 38  * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar
 39  *                 Custy
 40  * @run driver ExcludedClasses
 41  */
 42 
 43 import java.io.File;
 44 import java.lang.invoke.MethodHandles;
 45 import java.lang.invoke.MethodHandles.Lookup;
 46 import java.lang.reflect.InvocationHandler;
 47 import java.lang.reflect.Method;
 48 import java.lang.reflect.Proxy;
 49 import java.net.URL;
 50 import java.net.URLClassLoader;
 51 import java.nio.file.Files;
 52 import java.nio.file.Path;
 53 import java.security.ProtectionDomain;
 54 import java.util.Map;
 55 
 56 import jdk.jfr.Event;
 57 import jdk.test.lib.cds.CDSAppTester;
 58 import jdk.test.lib.helpers.ClassFileInstaller;
 59 import jdk.test.lib.process.OutputAnalyzer;
 60 
 61 public class ExcludedClasses {
 62     static final String appJar = ClassFileInstaller.getJarPath("app.jar");
 63     static final String mainClass = "TestApp";
 64 
 65     public static void main(String[] args) throws Exception {
 66         {
 67           Tester tester = new Tester();
 68           tester.run(new String[] {"AOT"} );
 69         }
 70         {
 71           Tester tester = new Tester();
 72           tester.run(new String[] {"LEYDEN"} );
 73         }
 74     }
 75 
 76     static class Tester extends CDSAppTester {
 77         public Tester() {
 78             super(mainClass);;
 79         }
 80 
 81         @Override
 82         public String classpath(RunMode runMode) {
 83             return appJar;
 84         }
 85 
 86         @Override
 87         public String[] vmArgs(RunMode runMode) {
 88             return new String[] {
 89                 "-Xlog:cds+resolve=trace",
 90                 //TEMP: uncomment the next line to see the TrainingData::_archived_training_data_dictionary
 91                 //"-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintTrainingInfo",
 92             };
 93         }
 94 
 95         @Override
 96         public String[] appCommandLine(RunMode runMode) {
 97             return new String[] {
 98                 mainClass, runMode.name()
 99             };
100         }
101 
102         @Override
103         public void checkExecution(OutputAnalyzer out, RunMode runMode) {
104             switch (runMode) {
105             case RunMode.TRAINING:
106             case RunMode.TRAINING0:
107             case RunMode.DUMP_STATIC:
108               //out.shouldMatch("cds,resolve.*archived field.*TestApp.Foo => TestApp.Foo.Bar.f:I");
109                 out.shouldNotMatch("cds,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I");
110             }
111         }
112     }
113 }
114 
115 class TestApp {
116     static Object custInstance;
117 
118     public static void main(String args[]) throws Exception {
119         // In new workflow, classes from custom loaders are passed from the preimage
120         // to the final image. See ClassPrelinker::record_unregistered_klasses().
121         custInstance = initFromCustomLoader();
122         System.out.println("Counter = " + Foo.hotSpot());
123     }
124 
125     static Object initFromCustomLoader() throws Exception {
126         String path = "cust.jar";
127         URL url = new File(path).toURI().toURL();
128         URL[] urls = new URL[] {url};
129         URLClassLoader urlClassLoader =
130             new URLClassLoader("MyLoader", urls, null);
131         Class c = Class.forName("Custy", true, urlClassLoader);
132         return c.newInstance();
133     }
134 
135     static class MyInvocationHandler implements InvocationHandler {
136         volatile static int cnt;
137 
138         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
139             long start = System.currentTimeMillis();
140             while (System.currentTimeMillis() - start < 20) {
141                 cnt += 2;
142                 for (int i = 0; i < 1000; i++) {
143                     int n = cnt - 2;
144                     if (n < 2) {
145                         n = 2;
146                     }
147                     cnt += (i + cnt) % n + cnt % 2;
148                 }
149             }
150             return Integer.valueOf(cnt);
151         }
152     }
153 
154     static class Foo {
155         volatile static int counter;
156         static Class c = ShouldBeExcluded.class;
157 
158         static Map mapProxy = (Map) Proxy.newProxyInstance(
159             Foo.class.getClassLoader(), 
160             new Class[] { Map.class },
161             new MyInvocationHandler());
162 
163         static int hotSpot() {
164             ShouldBeExcluded s = new ShouldBeExcluded();
165             Bar b = new Bar();
166 
167             long start = System.currentTimeMillis();
168             while (System.currentTimeMillis() - start < 1000) {
169                 lambdaHotSpot();
170                 s.hotSpot2();
171                 b.hotSpot3();
172 
173                 // Currently, generated proxy classes are excluded from the CDS archive
174                 Integer i = (Integer)mapProxy.get(null);
175                 counter += i.intValue();
176 
177 
178                 if (custInstance != null) {
179                     // For new workflow only:
180                     // Currently, classes loaded by custom loaders are included in the preimage run
181                     // but excluded from the final image.
182                     counter += custInstance.equals(null) ? 1 : 2;
183                 }
184             }
185 
186             return counter + s.m() + s.f + b.m() + b.f;
187         }
188 
189         static void f() {
190             if (counter % 2 == 1) {
191                 counter ++;
192             }
193         }
194 
195         // Lambda classes should be excluded from new workflow training run
196         static void lambdaHotSpot() {
197             long start = System.currentTimeMillis();
198             while (System.currentTimeMillis() - start < 20) {
199                 doit(() -> {
200                         counter ++;
201                     });
202             }
203         }
204 
205         static void doit(Runnable r) {
206             r.run();
207         }
208 
209         // All subclasses of jdk.jfr.Event are excluded from the CDS archive.
210         static class ShouldBeExcluded extends jdk.jfr.Event {
211             int f = (int)(System.currentTimeMillis()) + 123;
212             int m() {
213                 return f + 456;
214             }
215 
216             void hotSpot2() {
217                 long start = System.currentTimeMillis();
218                 while (System.currentTimeMillis() - start < 20) {
219                     for (int i = 0; i < 50000; i++) {
220                         counter += i;
221                     }
222                     f();
223                 }
224             }
225         }
226 
227         static class Bar {
228             int f = (int)(System.currentTimeMillis()) + 123;
229             int m() {
230                 return f + 456;
231             }
232 
233             void hotSpot3() {
234                 long start = System.currentTimeMillis();
235                 while (System.currentTimeMillis() - start < 20) {
236                     for (int i = 0; i < 50000; i++) {
237                         counter += i;
238                     }
239                     f();
240                 }
241             }
242         }
243     }
244 }