30 import jdk.test.lib.cds.CDSTestUtils; |
30 import jdk.test.lib.cds.CDSTestUtils; |
31 import jdk.test.lib.cds.CDSTestUtils.Result; |
31 import jdk.test.lib.cds.CDSTestUtils.Result; |
32 import jdk.test.lib.process.ProcessTools; |
32 import jdk.test.lib.process.ProcessTools; |
33 import jdk.test.lib.process.OutputAnalyzer; |
33 import jdk.test.lib.process.OutputAnalyzer; |
34 import java.io.File; |
34 import java.io.File; |
|
35 import java.io.FileInputStream; |
|
36 import java.io.FileOutputStream; |
|
37 import java.io.InputStream; |
|
38 import java.net.URI; |
|
39 import java.nio.file.DirectoryStream; |
|
40 import java.nio.file.Files; |
|
41 import java.nio.file.FileSystem; |
|
42 import java.nio.file.FileSystems; |
|
43 import java.nio.file.Path; |
35 import java.text.SimpleDateFormat; |
44 import java.text.SimpleDateFormat; |
36 import java.util.Arrays; |
45 import java.util.Arrays; |
37 import java.util.ArrayList; |
46 import java.util.ArrayList; |
38 import java.util.Date; |
47 import java.util.Date; |
|
48 import java.util.Enumeration; |
|
49 import java.util.regex.Matcher; |
|
50 import java.util.regex.Pattern; |
|
51 import java.util.zip.ZipEntry; |
|
52 import java.util.zip.ZipFile; |
|
53 import java.util.zip.ZipOutputStream; |
|
54 import jtreg.SkippedException; |
|
55 import cdsutils.DynamicDumpHelper; |
|
56 |
39 |
57 |
40 /** |
58 /** |
41 * This is a test utility class for common AppCDS test functionality. |
59 * This is a test utility class for common AppCDS test functionality. |
42 * |
60 * |
43 * Various methods use (String ...) for passing VM options. Note that the order |
61 * Various methods use (String ...) for passing VM options. Note that the order |
62 private static String currentArchiveName; |
80 private static String currentArchiveName; |
63 |
81 |
64 // Call this method to start new archive with new unique name |
82 // Call this method to start new archive with new unique name |
65 public static void startNewArchiveName() { |
83 public static void startNewArchiveName() { |
66 deletePriorArchives(); |
84 deletePriorArchives(); |
67 currentArchiveName = JSA_FILE_PREFIX + |
85 currentArchiveName = getNewArchiveName(); |
68 timeStampFormat.format(new Date()) + ".jsa"; |
|
69 } |
86 } |
70 |
87 |
71 // Call this method to get current archive name |
88 // Call this method to get current archive name |
72 public static String getCurrentArchiveName() { |
89 public static String getCurrentArchiveName() { |
73 return currentArchiveName; |
90 return currentArchiveName; |
|
91 } |
|
92 |
|
93 public static String getNewArchiveName() { |
|
94 return getNewArchiveName(null); |
|
95 } |
|
96 |
|
97 public static String getNewArchiveName(String stem) { |
|
98 if (stem == null) { |
|
99 stem = "appcds"; |
|
100 } |
|
101 return JSA_FILE_PREFIX + stem + "-" + |
|
102 timeStampFormat.format(new Date()) + ".jsa"; |
74 } |
103 } |
75 |
104 |
76 // Attempt to clean old archives to preserve space |
105 // Attempt to clean old archives to preserve space |
77 // Archives are large artifacts (20Mb or more), and much larger than |
106 // Archives are large artifacts (20Mb or more), and much larger than |
78 // most other artifacts created in jtreg testing. |
107 // most other artifacts created in jtreg testing. |
90 System.out.println("deletePriorArchives(): delete failed for file " + name); |
119 System.out.println("deletePriorArchives(): delete failed for file " + name); |
91 } |
120 } |
92 } |
121 } |
93 } |
122 } |
94 |
123 |
95 |
|
96 // Create AppCDS archive using most common args - convenience method |
124 // Create AppCDS archive using most common args - convenience method |
97 // Legacy name preserved for compatibility |
125 // Legacy name preserved for compatibility |
98 public static OutputAnalyzer dump(String appJar, String classList[], |
126 public static OutputAnalyzer dump(String appJar, String classList[], |
99 String... suffix) throws Exception { |
127 String... suffix) throws Exception { |
100 return createArchive(appJar, classList, suffix); |
128 return createArchive(appJar, classList, suffix); |
108 opts.setClassList(classList); |
136 opts.setClassList(classList); |
109 opts.addSuffix(suffix); |
137 opts.addSuffix(suffix); |
110 return createArchive(opts); |
138 return createArchive(opts); |
111 } |
139 } |
112 |
140 |
|
141 // Simulate -Xshare:dump with -XX:ArchiveClassesAtExit. See comments around patchJarForDynamicDump() |
|
142 private static final Class tmp = DynamicDumpHelper.class; |
|
143 |
113 // Create AppCDS archive using appcds options |
144 // Create AppCDS archive using appcds options |
114 public static OutputAnalyzer createArchive(AppCDSOptions opts) |
145 public static OutputAnalyzer createArchive(AppCDSOptions opts) |
115 throws Exception { |
146 throws Exception { |
116 |
|
117 ArrayList<String> cmd = new ArrayList<String>(); |
147 ArrayList<String> cmd = new ArrayList<String>(); |
118 startNewArchiveName(); |
148 startNewArchiveName(); |
119 |
149 |
120 for (String p : opts.prefix) cmd.add(p); |
150 for (String p : opts.prefix) cmd.add(p); |
121 |
151 |
122 if (opts.appJar != null) { |
152 if (opts.appJar != null) { |
123 cmd.add("-cp"); |
153 cmd.add("-cp"); |
124 cmd.add(opts.appJar); |
154 cmd.add(opts.appJar); |
|
155 File jf = new File(opts.appJar); |
|
156 if (DYNAMIC_DUMP && !jf.isDirectory()) { |
|
157 patchJarForDynamicDump(opts.appJar); |
|
158 } |
125 } else { |
159 } else { |
126 cmd.add("-Djava.class.path="); |
160 cmd.add("-Djava.class.path="); |
127 } |
161 } |
128 |
162 |
129 cmd.add("-Xshare:dump"); |
163 if (opts.archiveName == null) { |
130 |
|
131 if (opts.archiveName == null) |
|
132 opts.archiveName = getCurrentArchiveName(); |
164 opts.archiveName = getCurrentArchiveName(); |
133 |
165 } |
134 cmd.add("-XX:SharedArchiveFile=" + opts.archiveName); |
166 |
135 |
167 if (DYNAMIC_DUMP) { |
136 if (opts.classList != null) { |
168 cmd.add("-Xshare:on"); |
137 File classListFile = makeClassList(opts.classList); |
169 cmd.add("-XX:ArchiveClassesAtExit=" + opts.archiveName); |
138 cmd.add("-XX:ExtraSharedClassListFile=" + classListFile.getPath()); |
170 |
139 } |
171 cmd.add("-Xlog:cds"); |
140 |
172 cmd.add("-Xlog:cds+dynamic"); |
141 for (String s : opts.suffix) cmd.add(s); |
173 boolean mainModuleSpecified = false; |
|
174 boolean patchModuleSpecified = false; |
|
175 for (String s : opts.suffix) { |
|
176 if (s.length() == 0) { |
|
177 continue; |
|
178 } |
|
179 if (s.equals("-m")) { |
|
180 mainModuleSpecified = true; |
|
181 } |
|
182 if (s.startsWith("--patch-module=")) { |
|
183 patchModuleSpecified = true; |
|
184 } |
|
185 cmd.add(s); |
|
186 } |
|
187 |
|
188 if (opts.appJar != null) { |
|
189 // classlist is supported only when we have a Jar file to patch (to insert |
|
190 // cdsutils.DynamicDumpHelper) |
|
191 if (opts.classList == null) { |
|
192 throw new RuntimeException("test.dynamic.dump requires classList file"); |
|
193 } |
|
194 |
|
195 if (!mainModuleSpecified && !patchModuleSpecified) { |
|
196 cmd.add("cdsutils.DynamicDumpHelper"); |
|
197 File classListFile = makeClassList(opts.classList); |
|
198 cmd.add(classListFile.getPath()); |
|
199 } |
|
200 } else { |
|
201 if (!mainModuleSpecified && !patchModuleSpecified) { |
|
202 // If you have an empty classpath, you cannot specify a classlist! |
|
203 if (opts.classList != null && opts.classList.length > 0) { |
|
204 throw new RuntimeException("test.dynamic.dump not supported empty classpath with non-empty classlist"); |
|
205 } |
|
206 cmd.add("-version"); |
|
207 } |
|
208 } |
|
209 } else { |
|
210 // static dump |
|
211 cmd.add("-Xshare:dump"); |
|
212 cmd.add("-XX:SharedArchiveFile=" + opts.archiveName); |
|
213 |
|
214 if (opts.classList != null) { |
|
215 File classListFile = makeClassList(opts.classList); |
|
216 cmd.add("-XX:ExtraSharedClassListFile=" + classListFile.getPath()); |
|
217 } |
|
218 for (String s : opts.suffix) { |
|
219 cmd.add(s); |
|
220 } |
|
221 } |
142 |
222 |
143 String[] cmdLine = cmd.toArray(new String[cmd.size()]); |
223 String[] cmdLine = cmd.toArray(new String[cmd.size()]); |
144 ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine); |
224 ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine); |
145 return executeAndLog(pb, "dump"); |
225 return executeAndLog(pb, "dump"); |
146 } |
226 } |
152 // -Dtest.cds.run.with.jfr=true |
232 // -Dtest.cds.run.with.jfr=true |
153 // |
233 // |
154 // Some AppCDS tests are not compatible with this mode. See the group |
234 // Some AppCDS tests are not compatible with this mode. See the group |
155 // hotspot_appcds_with_jfr in ../../TEST.ROOT for details. |
235 // hotspot_appcds_with_jfr in ../../TEST.ROOT for details. |
156 private static final boolean RUN_WITH_JFR = Boolean.getBoolean("test.cds.run.with.jfr"); |
236 private static final boolean RUN_WITH_JFR = Boolean.getBoolean("test.cds.run.with.jfr"); |
|
237 // This method simulates -Xshare:dump with -XX:ArchiveClassesAtExit. This way, we |
|
238 // can re-use many tests (outside of the ./dynamicArchive directory) for testing |
|
239 // general features of JDK-8215311 (JEP 350: Dynamic CDS Archives). |
|
240 // |
|
241 // We insert the cdsutils/DynamicDumpHelper.class into the first Jar file in |
|
242 // the classpath. We use this class to load all the classes specified in the classlist. |
|
243 // |
|
244 // There's no need to change the run-time command-line: in this special mode, two |
|
245 // archives are involved. The command-line specifies only the top archive. However, |
|
246 // the location of the base archive is recorded in the top archive, so it can be |
|
247 // determined by the JVM at runtime start-up. |
|
248 // |
|
249 // To run in this special mode, specify the following in your jtreg command-line |
|
250 // -Dtest.dynamic.cds.archive=true |
|
251 // |
|
252 // Note that some tests are not compatible with this special mode, including |
|
253 // + Tests in ./dynamicArchive: these tests are specifically written for |
|
254 // dynamic archive, and do not use TestCommon.createArchive(), which works |
|
255 // together with patchJarForDynamicDump(). |
|
256 // + Tests related to cached objects and shared strings: dynamic dumping |
|
257 // does not support these. |
|
258 // + Custom loader tests: DynamicDumpHelper doesn't support the required |
|
259 // classlist syntax. (FIXME). |
|
260 // + Extra symbols and extra strings. |
|
261 // See the hotspot_appcds_dynamic in ../../TEST.ROOT for details. |
|
262 // |
|
263 // To run all tests that are compatible with this mode: |
|
264 // cd test/hotspot/jtreg |
|
265 // jtreg -Dtest.dynamic.cds.archive=true :hotspot_appcds_dynamic |
|
266 // |
|
267 private static void patchJarForDynamicDump(String cp) throws Exception { |
|
268 System.out.println("patchJarForDynamicDump: classpath = " + cp); |
|
269 String firstJar = cp; |
|
270 int n = firstJar.indexOf(File.pathSeparator); |
|
271 if (n > 0) { |
|
272 firstJar = firstJar.substring(0, n); |
|
273 } |
|
274 String classDir = System.getProperty("test.classes"); |
|
275 String expected1 = classDir + File.separator; |
|
276 String expected2 = System.getProperty("user.dir") + File.separator; |
|
277 |
|
278 if (!firstJar.startsWith(expected1) && !firstJar.startsWith(expected2)) { |
|
279 throw new RuntimeException("FIXME: jar file not at a supported location ('" |
|
280 + expected1 + "', or '" + expected2 + "'): " + firstJar); |
|
281 } |
|
282 |
|
283 String replaceJar = firstJar + ".tmp"; |
|
284 String patchClass = "cdsutils/DynamicDumpHelper.class"; |
|
285 ZipFile zipFile = new ZipFile(firstJar); |
|
286 byte[] buf = new byte[1024]; |
|
287 int len; |
|
288 if (zipFile.getEntry(patchClass) == null) { |
|
289 FileOutputStream fout = new FileOutputStream(replaceJar); |
|
290 final ZipOutputStream zos = new ZipOutputStream(fout); |
|
291 |
|
292 zos.putNextEntry(new ZipEntry(patchClass)); |
|
293 InputStream is = new FileInputStream(classDir + File.separator + patchClass); |
|
294 while ((len = (is.read(buf))) > 0) { |
|
295 zos.write(buf, 0, len); |
|
296 } |
|
297 zos.closeEntry(); |
|
298 is.close(); |
|
299 |
|
300 for (Enumeration e = zipFile.entries(); e.hasMoreElements(); ) { |
|
301 ZipEntry entryIn = (ZipEntry) e.nextElement(); |
|
302 zos.putNextEntry(entryIn); |
|
303 is = zipFile.getInputStream(entryIn); |
|
304 while ((len = is.read(buf)) > 0) { |
|
305 zos.write(buf, 0, len); |
|
306 } |
|
307 zos.closeEntry(); |
|
308 is.close(); |
|
309 } |
|
310 |
|
311 zos.close(); |
|
312 fout.close(); |
|
313 zipFile.close(); |
|
314 |
|
315 File oldFile = new File(firstJar); |
|
316 File newFile = new File(replaceJar); |
|
317 oldFile.delete(); |
|
318 newFile.renameTo(oldFile); |
|
319 System.out.println("firstJar = " + firstJar + " Modified"); |
|
320 } else { |
|
321 System.out.println("firstJar = " + firstJar); |
|
322 } |
|
323 } |
157 |
324 |
158 // Execute JVM using AppCDS archive with specified AppCDSOptions |
325 // Execute JVM using AppCDS archive with specified AppCDSOptions |
159 public static OutputAnalyzer runWithArchive(AppCDSOptions opts) |
326 public static OutputAnalyzer runWithArchive(AppCDSOptions opts) |
160 throws Exception { |
327 throws Exception { |
161 |
328 |
258 AppCDSOptions opts = makeModuleOptions(prefix, upgrademodulepath, modulepath, |
425 AppCDSOptions opts = makeModuleOptions(prefix, upgrademodulepath, modulepath, |
259 mid, testClassArgs); |
426 mid, testClassArgs); |
260 return runWithArchive(opts); |
427 return runWithArchive(opts); |
261 } |
428 } |
262 |
429 |
263 |
|
264 // A common operation: dump, then check results |
430 // A common operation: dump, then check results |
265 public static OutputAnalyzer testDump(String appJar, String classList[], |
431 public static OutputAnalyzer testDump(String appJar, String classList[], |
266 String... suffix) throws Exception { |
432 String... suffix) throws Exception { |
267 OutputAnalyzer output = dump(appJar, classList, suffix); |
433 OutputAnalyzer output = dump(appJar, classList, suffix); |
268 output.shouldContain("Loading classes to share"); |
434 if (DYNAMIC_DUMP) { |
|
435 if (isUnableToMap(output)) { |
|
436 throw new SkippedException(UnableToMapMsg); |
|
437 } |
|
438 output.shouldContain("Written dynamic archive"); |
|
439 } else { |
|
440 output.shouldContain("Loading classes to share"); |
|
441 } |
269 output.shouldHaveExitValue(0); |
442 output.shouldHaveExitValue(0); |
270 return output; |
443 return output; |
271 } |
444 } |
272 |
445 |
273 |
446 |