1 /*
  2  * Copyright (c) 2023, 2024, 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 LEYDEN
 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         Tester t = new Tester();
 67         t.run(args);
 68     }
 69 
 70     static class Tester extends CDSAppTester {
 71         public Tester() {
 72             super(mainClass);;
 73         }
 74 
 75         @Override
 76         public String classpath(RunMode runMode) {
 77             return appJar;
 78         }
 79 
 80         @Override
 81         public String[] vmArgs(RunMode runMode) {
 82             return new String[] {
 83                 "-Xlog:cds+resolve=trace",
 84                 //TEMP: uncomment the next line to see the TrainingData::_archived_training_data_dictionary
 85                 //"-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintTrainingInfo",
 86             };
 87         }
 88 
 89         @Override
 90         public String[] appCommandLine(RunMode runMode) {
 91             return new String[] {
 92                 mainClass, runMode.name()
 93             };
 94         }
 95 
 96         @Override
 97         public void checkExecution(OutputAnalyzer out, RunMode runMode) {
 98             switch (runMode) {
 99             case RunMode.TRAINING:
100             case RunMode.TRAINING0:
101             case RunMode.TRAINING1:
102             case RunMode.DUMP_STATIC:
103                 out.shouldMatch("cds,resolve.*archived field.*TestApp.Foo => TestApp.Foo.Bar.f:I");
104                 out.shouldNotMatch("cds,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I");
105             }
106         }
107     }
108 }
109 
110 class TestApp {
111     static Object custInstance;
112 
113     public static void main(String args[]) throws Exception {
114         // In new workflow, classes from custom loaders are passed from the preimage
115         // to the final image. See ClassPrelinker::record_unregistered_klasses().
116         custInstance = initFromCustomLoader();
117         System.out.println("Counter = " + Foo.hotSpot());
118     }
119 
120     static Object initFromCustomLoader() throws Exception {
121         String path = "cust.jar";
122         URL url = new File(path).toURI().toURL();
123         URL[] urls = new URL[] {url};
124         URLClassLoader urlClassLoader =
125             new URLClassLoader("MyLoader", urls, null);
126         Class c = Class.forName("Custy", true, urlClassLoader);
127         return c.newInstance();
128     }
129 
130     static class MyInvocationHandler implements InvocationHandler {
131         volatile static int cnt;
132 
133         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
134             long start = System.currentTimeMillis();
135             while (System.currentTimeMillis() - start < 20) {
136                 cnt += 2;
137                 for (int i = 0; i < 1000; i++) {
138                     int n = cnt - 2;
139                     if (n < 2) {
140                         n = 2;
141                     }
142                     cnt += (i + cnt) % n + cnt % 2;
143                 }
144             }
145             return Integer.valueOf(cnt);
146         }
147     }
148 
149     static class Foo {
150         volatile static int counter;
151         static Class c = ShouldBeExcluded.class;
152 
153         static Map mapProxy = (Map) Proxy.newProxyInstance(
154             Foo.class.getClassLoader(), 
155             new Class[] { Map.class },
156             new MyInvocationHandler());
157 
158         static int hotSpot() {
159             ShouldBeExcluded s = new ShouldBeExcluded();
160             Bar b = new Bar();
161 
162             long start = System.currentTimeMillis();
163             while (System.currentTimeMillis() - start < 1000) {
164                 lambdaHotSpot();
165                 s.hotSpot2();
166                 b.hotSpot3();
167 
168                 // Currently, generated proxy classes are excluded from the CDS archive
169                 Integer i = (Integer)mapProxy.get(null);
170                 counter += i.intValue();
171 
172 
173                 if (custInstance != null) {
174                     // For new workflow only:
175                     // Currently, classes loaded by custom loaders are included in the preimage run
176                     // but excluded from the final image.
177                     counter += custInstance.equals(null) ? 1 : 2;
178                 }
179             }
180 
181             return counter + s.m() + s.f + b.m() + b.f;
182         }
183 
184         static void f() {
185             if (counter % 2 == 1) {
186                 counter ++;
187             }
188         }
189 
190         // Lambda classes should be excluded from new workflow training run
191         static void lambdaHotSpot() {
192             long start = System.currentTimeMillis();
193             while (System.currentTimeMillis() - start < 20) {
194                 doit(() -> {
195                         counter ++;
196                     });
197             }
198         }
199 
200         static void doit(Runnable r) {
201             r.run();
202         }
203 
204         // All subclasses of jdk.jfr.Event are excluded from the CDS archive.
205         static class ShouldBeExcluded extends jdk.jfr.Event {
206             int f = (int)(System.currentTimeMillis()) + 123;
207             int m() {
208                 return f + 456;
209             }
210 
211             void hotSpot2() {
212                 long start = System.currentTimeMillis();
213                 while (System.currentTimeMillis() - start < 20) {
214                     for (int i = 0; i < 50000; i++) {
215                         counter += i;
216                     }
217                     f();
218                 }
219             }
220         }
221 
222         static class Bar {
223             int f = (int)(System.currentTimeMillis()) + 123;
224             int m() {
225                 return f + 456;
226             }
227 
228             void hotSpot3() {
229                 long start = System.currentTimeMillis();
230                 while (System.currentTimeMillis() - start < 20) {
231                     for (int i = 0; i < 50000; i++) {
232                         counter += i;
233                     }
234                     f();
235                 }
236             }
237         }
238     }
239 }