1 /*
  2  * Copyright (c) 2022, 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  * @key randomness
 28  * @summary test dynamic dump meanwhile output loaded class list
 29  * @bug 8279009 8275084
 30  * @requires vm.cds
 31  * @requires vm.cds.custom.loaders
 32  * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
 33  * @compile test-classes/Hello.java ClassSpecializerTestApp.java ClassListWithCustomClassNoSource.java
 34  * @run main/othervm TestDumpClassListSource
 35  */
 36 
 37 /* Test two senarios:
 38  *   1. ClassSpecializerTestApp.java:
 39  *      Test case for bug 8275084, make sure the filtering of source class to
 40  *      dumped class list.
 41  *   2. ClassListWithCustomClassNoSource: test custom class loader
 42  *      2.1 class loaded without source.
 43  *      2.2 class loaded with ProtectionDomain set as same as main class.
 44  *      2.3 class loaded by custom loader from shared space.
 45  */
 46 
 47 import java.io.BufferedReader;
 48 import java.io.FileReader;
 49 import java.io.File;
 50 
 51 import java.nio.file.Files;
 52 import java.nio.file.Paths;
 53 import java.util.List;
 54 import java.util.regex.Matcher;
 55 import java.util.regex.Pattern;
 56 import java.util.stream.Collectors;
 57 
 58 import jdk.test.lib.process.OutputAnalyzer;
 59 import jdk.test.lib.process.ProcessTools;
 60 import jdk.test.lib.cds.CDSTestUtils;
 61 
 62 public class TestDumpClassListSource {
 63     private static final boolean EXPECT_MATCH = true;
 64     private static final boolean EXPECT_NOMATCH  = !EXPECT_MATCH;
 65 
 66     private static void checkMatch(String file, String regexp, boolean expectMatch, String exceptionMessage) throws Exception {
 67         String listData = new String(Files.readAllBytes(Paths.get(file)));
 68         Pattern pattern = Pattern.compile(regexp, Pattern.MULTILINE);
 69         Matcher matcher = pattern.matcher(listData);
 70         boolean found   = matcher.find();
 71         if (expectMatch) {
 72             if (!found) {
 73                 throw new RuntimeException(exceptionMessage);
 74             }
 75         } else {
 76             if (found) {
 77                 throw new RuntimeException(exceptionMessage);
 78             }
 79         }
 80     }
 81 
 82     static final String mainInvokeClass = "ClassSpecializerTestApp";
 83     static final String mainCutomClass  = "ClassListWithCustomClassNoSource";
 84     static final String sourceTarget    = "_ClassSpecializer_generateConcreteSpeciesCode";
 85 
 86     private static void checkFileExistence(String type, File file) throws Exception {
 87         if (!file.exists()) {
 88             throw new RuntimeException(type + " file " + file.getName() + " should be created");
 89         }
 90     }
 91 
 92     public static void main(String[] args) throws Exception {
 93         String listFileName = "test-classlist.list";
 94         String archiveName  = "test-dynamic.jsa";
 95         String jarFile = JarBuilder.build("test-hello", "ClassSpecializerTestApp", "ClassListWithCustomClassNoSource",
 96                                           "ClassListWithCustomClassNoSource$CL", "Hello");
 97         // 1. Invoke lambda
 98         File fileList = new File(listFileName);
 99         if (fileList.exists()) {
100             fileList.delete();
101         }
102         File fileArchive = new File(archiveName);
103         if (fileArchive.exists()) {
104             fileArchive.delete();
105         }
106         String[] launchArgs  = {
107                 "-Xshare:auto",
108                 "-XX:DumpLoadedClassList=" + listFileName,
109                 "-XX:ArchiveClassesAtExit=" + archiveName,
110                 "-Xlog:cds",
111                 "-Xlog:cds+lambda",
112                 "-cp",
113                 jarFile,
114                 mainInvokeClass};
115         ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(launchArgs);
116         OutputAnalyzer output = TestCommon.executeAndLog(pb, "invoke-class");
117 
118         checkFileExistence("Archive", fileArchive);
119         checkFileExistence("ClassList", fileList);
120 
121         output.shouldHaveExitValue(0);
122         checkMatch(listFileName, sourceTarget, EXPECT_NOMATCH, "Failed to filter " + sourceTarget + " in class list file");
123 
124         fileArchive.delete();
125         fileList.delete();
126 
127         // 2. Custom loaded class
128         //    2.1 test in memory class generation without source
129         launchArgs  = new String[] {
130                 "-Xshare:auto",
131                 "-XX:DumpLoadedClassList=" + listFileName,
132                 "-XX:ArchiveClassesAtExit=" + archiveName,
133                 "-Xlog:cds",
134                 "-Xlog:cds+lambda",
135                 "-Xlog:class+path=info",
136                  "-cp",
137                 jarFile,
138                 mainCutomClass,
139                 "1"};
140         pb = ProcessTools.createJavaProcessBuilder(launchArgs);
141         output = TestCommon.executeAndLog(pb, "custom-nosource");
142 
143         checkFileExistence("Archive", fileArchive);
144         checkFileExistence("ClassList", fileList);
145 
146         output.shouldHaveExitValue(0);
147         checkMatch(listFileName, sourceTarget, EXPECT_NOMATCH, "Failed to filter " + sourceTarget + " in class list file");
148         checkMatch(listFileName, "Hello", EXPECT_NOMATCH, "Hello should not be logged in class list file");
149 
150         fileArchive.delete();
151         fileList.delete();
152 
153         //    2.2 test in memory class with ProtectionDomain as main class.
154         //    "Hello" will be printed in list file and its source set as main class.
155         launchArgs  = new String[] {
156                 "-Xshare:auto",
157                 "-XX:DumpLoadedClassList=" + listFileName,
158                 "-XX:ArchiveClassesAtExit=" + archiveName,
159                 "-Xlog:cds",
160                 "-Xlog:cds+lambda",
161                 "-Xlog:class+path=info",
162                  "-cp",
163                 jarFile,
164                 mainCutomClass,
165                 "2"};
166         pb = ProcessTools.createJavaProcessBuilder(launchArgs);
167         output = TestCommon.executeAndLog(pb, "custom-nosource");
168 
169         checkFileExistence("Archive", fileArchive);
170         checkFileExistence("ClassList", fileList);
171 
172         output.shouldHaveExitValue(0);
173         checkMatch(listFileName, sourceTarget, EXPECT_NOMATCH, "Failed to filter " + sourceTarget + " in class list file");
174         checkMatch(listFileName, "Hello", EXPECT_MATCH, "Hello should be logged in class list file");
175 
176         fileArchive.delete();
177         fileList.delete();
178 
179         //    2.3 class loaded by custom loader from shared space.
180         //      2.3.1 dump class list
181         launchArgs = new String[] {
182                 "-XX:DumpLoadedClassList=" + listFileName,
183                  "-cp",
184                 jarFile,
185                 mainCutomClass,
186                 "3"};
187         pb = ProcessTools.createJavaProcessBuilder(launchArgs);
188         output = TestCommon.executeAndLog(pb, "custom-dump-classlist");
189 
190         checkFileExistence("ClassList", fileList);
191 
192         checkMatch(listFileName, "Hello id: [0-9]+ super: [0-9]+ interfaces: [0-9]+ source: .*/test-hello.jar", EXPECT_MATCH,
193                    "Class Hello should be printed in classlist");
194         //      2.3.2 dump shared archive based on listFileName
195         String archive = "test-hello.jsa";
196         File archiveFile = new File(archive);
197         if (archiveFile.exists()) {
198             archiveFile.delete();
199         }
200         launchArgs = new String[] {
201                 "-Xshare:dump",
202                 "-XX:SharedClassListFile=" + listFileName,
203                 "-XX:SharedArchiveFile=" + archive,
204                  "-cp",
205                 jarFile,
206                 mainCutomClass,
207                 "3"};
208         pb = ProcessTools.createJavaProcessBuilder(launchArgs);
209         output = TestCommon.executeAndLog(pb, "custom-dump");
210 
211         checkFileExistence("Archive", archiveFile);
212 
213         //       2.3.3 run with the shared archive and -XX:DumpLoadedClassList
214         //             Hello should not be printed out in class list file.
215         String classList = "new-test-list.list";
216         File newFile = new File(classList);
217         if (newFile.exists()) {
218             newFile.delete();
219         }
220         launchArgs = new String[] {
221                 "-Xshare:on",
222                 "-XX:SharedArchiveFile=" + archive,
223                 "-XX:DumpLoadedClassList=" + classList,
224                  "-cp",
225                 jarFile,
226                 mainCutomClass,
227                 "3"};
228         pb = ProcessTools.createJavaProcessBuilder(launchArgs);
229         output = TestCommon.executeAndLog(pb, "custom-share");
230 
231         checkFileExistence("ClassList", newFile);
232         checkMatch(classList, "Hello id: ?", EXPECT_NOMATCH, "Failed to filter custom loaded class Hello from class list");
233     }
234 }