Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/tools/jlink/plugins/StripNativeDebugSymbolsPlugin/StripNativeDebugSymbolsPluginTest.java
41155 views
1
/*
2
* Copyright (c) 2019, Red Hat, Inc.
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. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
import java.io.BufferedWriter;
26
import java.io.File;
27
import java.io.IOException;
28
import java.io.InputStream;
29
import java.io.PrintWriter;
30
import java.io.StringWriter;
31
import java.nio.file.FileVisitResult;
32
import java.nio.file.Files;
33
import java.nio.file.NoSuchFileException;
34
import java.nio.file.Path;
35
import java.nio.file.Paths;
36
import java.nio.file.SimpleFileVisitor;
37
import java.nio.file.attribute.BasicFileAttributes;
38
import java.util.ArrayList;
39
import java.util.Arrays;
40
import java.util.List;
41
import java.util.Map;
42
import java.util.Scanner;
43
import java.util.spi.ToolProvider;
44
import java.util.stream.Collectors;
45
import java.util.stream.Stream;
46
47
import jdk.test.lib.compiler.CompilerUtils;
48
import jdk.tools.jlink.internal.ResourcePoolManager;
49
import jdk.tools.jlink.internal.plugins.StripNativeDebugSymbolsPlugin;
50
import jdk.tools.jlink.internal.plugins.StripNativeDebugSymbolsPlugin.ObjCopyCmdBuilder;
51
import jdk.tools.jlink.plugin.ResourcePool;
52
import jdk.tools.jlink.plugin.ResourcePoolEntry;
53
54
/*
55
* @test
56
* @requires os.family == "linux"
57
* @bug 8214796
58
* @summary Test --strip-native-debug-symbols plugin
59
* @library /test/lib
60
* @modules jdk.compiler
61
* jdk.jlink/jdk.tools.jlink.internal.plugins
62
* jdk.jlink/jdk.tools.jlink.internal
63
* jdk.jlink/jdk.tools.jlink.plugin
64
* @build jdk.test.lib.compiler.CompilerUtils FakeObjCopy
65
* @run main/othervm -Xmx1g StripNativeDebugSymbolsPluginTest
66
*/
67
public class StripNativeDebugSymbolsPluginTest {
68
69
private static final String OBJCOPY = "objcopy";
70
private static final String DEFAULT_OBJCOPY_CMD = OBJCOPY;
71
private static final String PLUGIN_NAME = "strip-native-debug-symbols";
72
private static final String MODULE_NAME_WITH_NATIVE = "fib";
73
private static final String JAVA_HOME = System.getProperty("java.home");
74
private static final String NATIVE_LIB_NAME = "libFib.so";
75
private static final Path JAVA_LIB_PATH = Paths.get(System.getProperty("java.library.path"));
76
private static final Path LIB_FIB_SRC = JAVA_LIB_PATH.resolve(NATIVE_LIB_NAME);
77
private static final String FIBJNI_CLASS_NAME = "FibJNI.java";
78
private static final Path JAVA_SRC_DIR = Paths.get(System.getProperty("test.src"))
79
.resolve("src")
80
.resolve(MODULE_NAME_WITH_NATIVE);
81
private static final Path FIBJNI_JAVA_CLASS = JAVA_SRC_DIR.resolve(FIBJNI_CLASS_NAME);
82
private static final String DEBUG_EXTENSION = "debug";
83
private static final long ORIG_LIB_FIB_SIZE = LIB_FIB_SRC.toFile().length();
84
private static final String FAKE_OBJ_COPY_LOG_FILE = "objcopy.log";
85
private static final String OBJCOPY_ONLY_DEBUG_SYMS_OPT = "-g";
86
private static final String OBJCOPY_ONLY_KEEP_DEBUG_SYMS_OPT = "--only-keep-debug";
87
private static final String OBJCOPY_ADD_DEBUG_LINK_OPT = "--add-gnu-debuglink";
88
89
///////////////////////////////////////////////////////////////////////////
90
//
91
// Tests which do NOT rely on objcopy being present on the test system
92
//
93
///////////////////////////////////////////////////////////////////////////
94
95
public void testPluginLoaded() {
96
List<String> output =
97
JLink.run("--list-plugins").output();
98
if (output.stream().anyMatch(s -> s.contains(PLUGIN_NAME))) {
99
System.out.println("DEBUG: " + PLUGIN_NAME + " plugin loaded as expected.");
100
} else {
101
throw new AssertionError("strip-native-debug-symbols plugin not in " +
102
"--list-plugins output.");
103
}
104
}
105
106
public void testConfigureFakeObjCopy() throws Exception {
107
configureConflictingOptions();
108
configureObjcopyWithOmit();
109
configureObjcopyWithKeep();
110
configureUnknownOptions();
111
configureMultipleTimesSamePlugin();
112
System.out.println("Test testConfigureFakeObjCopy() PASSED!");
113
}
114
115
private void configureMultipleTimesSamePlugin() throws Exception {
116
Map<String, String> keepDebug = Map.of(
117
StripNativeDebugSymbolsPlugin.NAME, "keep-debuginfo-files"
118
);
119
Map<String, String> excludeDebug = Map.of(
120
StripNativeDebugSymbolsPlugin.NAME, "exclude-debuginfo-files"
121
);
122
StripNativeDebugSymbolsPlugin plugin = createAndConfigPlugin(keepDebug);
123
try {
124
plugin.doConfigure(false, excludeDebug);
125
throw new AssertionError("should have thrown IAE for broken config: " +
126
keepDebug + " and " + excludeDebug);
127
} catch (IllegalArgumentException e) {
128
// pass
129
System.out.println("DEBUG: test threw IAE " + e.getMessage() +
130
" as expected.");
131
}
132
}
133
134
private void configureUnknownOptions() throws Exception {
135
Map<String, String> config = Map.of(
136
StripNativeDebugSymbolsPlugin.NAME, "foobar"
137
);
138
doConfigureUnknownOption(config);
139
config = Map.of(
140
StripNativeDebugSymbolsPlugin.NAME, "keep-debuginfo-files",
141
"foo", "bar" // unknown value
142
);
143
doConfigureUnknownOption(config);
144
}
145
146
private void doConfigureUnknownOption(Map<String, String> config) throws Exception {
147
try {
148
createAndConfigPlugin(config);
149
throw new AssertionError("should have thrown IAE for broken config: " + config);
150
} catch (IllegalArgumentException e) {
151
// pass
152
System.out.println("DEBUG: test threw IAE " + e.getMessage() +
153
" as expected.");
154
}
155
}
156
157
private void configureObjcopyWithKeep() throws Exception {
158
String objcopyPath = "foobar";
159
String debugExt = "debuginfo"; // that's the default value
160
Map<String, String> config = Map.of(
161
StripNativeDebugSymbolsPlugin.NAME, "keep-debuginfo-files",
162
"objcopy", objcopyPath
163
);
164
doKeepDebugInfoFakeObjCopyTest(config, debugExt, objcopyPath);
165
// Do it again combining options the other way round
166
debugExt = "testme";
167
config = Map.of(
168
StripNativeDebugSymbolsPlugin.NAME, "objcopy=" + objcopyPath,
169
"keep-debuginfo-files", debugExt
170
);
171
doKeepDebugInfoFakeObjCopyTest(config, debugExt, objcopyPath);
172
System.out.println("DEBUG: configureObjcopyWithKeep() PASSED!");
173
}
174
175
private void configureObjcopyWithOmit() throws Exception {
176
String objcopyPath = "something-non-standard";
177
Map<String, String> config = Map.of(
178
StripNativeDebugSymbolsPlugin.NAME, "exclude-debuginfo-files",
179
"objcopy", objcopyPath
180
);
181
doOmitDebugInfoFakeObjCopyTest(config, objcopyPath);
182
System.out.println("DEBUG: configureObjcopyWithOmit() PASSED!");
183
}
184
185
private void configureConflictingOptions() throws Exception {
186
Map<String, String> config = Map.of(
187
StripNativeDebugSymbolsPlugin.NAME, "exclude-debuginfo-files",
188
"keep-debuginfo-files", "foo-ext"
189
);
190
doConfigureConflictingOptions(config);
191
config = Map.of(
192
StripNativeDebugSymbolsPlugin.NAME, "exclude-debuginfo-files=bar",
193
"keep-debuginfo-files", "foo-ext"
194
);
195
doConfigureConflictingOptions(config);
196
}
197
198
private void doConfigureConflictingOptions(Map<String, String> config) throws Exception {
199
try {
200
createAndConfigPlugin(config);
201
throw new AssertionError("keep-debuginfo-files and exclude-debuginfo-files " +
202
" should have conflicted!");
203
} catch (IllegalArgumentException e) {
204
// pass
205
if (e.getMessage().contains("keep-debuginfo-files") &&
206
e.getMessage().contains("exclude-debuginfo-files")) {
207
System.out.println("DEBUG: test threw IAE " + e.getMessage() +
208
" as expected.");
209
} else {
210
throw new AssertionError("Unexpected IAE", e);
211
}
212
}
213
}
214
215
public void testTransformFakeObjCopyNoDebugInfoFiles() throws Exception {
216
Map<String, String> defaultConfig = Map.of(
217
StripNativeDebugSymbolsPlugin.NAME, "exclude-debuginfo-files"
218
);
219
doOmitDebugInfoFakeObjCopyTest(defaultConfig, DEFAULT_OBJCOPY_CMD);
220
System.out.println("testTransformFakeObjCopyNoDebugInfoFiles() PASSED!");
221
}
222
223
private void doOmitDebugInfoFakeObjCopyTest(Map<String, String> config,
224
String expectedObjCopy) throws Exception {
225
StripNativeDebugSymbolsPlugin plugin = createAndConfigPlugin(config, expectedObjCopy);
226
String binFile = "mybin";
227
String path = "/fib/bin/" + binFile;
228
ResourcePoolEntry debugEntry = createMockEntry(path,
229
ResourcePoolEntry.Type.NATIVE_CMD);
230
ResourcePoolManager inResources = new ResourcePoolManager();
231
ResourcePoolManager outResources = new ResourcePoolManager();
232
inResources.add(debugEntry);
233
ResourcePool output = plugin.transform(
234
inResources.resourcePool(),
235
outResources.resourcePoolBuilder());
236
// expect entry to be present
237
if (output.findEntry(path).isPresent()) {
238
System.out.println("DEBUG: File " + path + " present as exptected.");
239
} else {
240
throw new AssertionError("Test failed. Binary " + path +
241
" not present after stripping!");
242
}
243
verifyFakeObjCopyCalled(binFile);
244
}
245
246
public void testTransformFakeObjCopyKeepDebugInfoFiles() throws Exception {
247
Map<String, String> defaultConfig = Map.of(
248
StripNativeDebugSymbolsPlugin.NAME,
249
"keep-debuginfo-files=" + DEBUG_EXTENSION
250
);
251
doKeepDebugInfoFakeObjCopyTest(defaultConfig,
252
DEBUG_EXTENSION,
253
DEFAULT_OBJCOPY_CMD);
254
System.out.println("testTransformFakeObjCopyKeepDebugInfoFiles() PASSED!");
255
}
256
257
private void doKeepDebugInfoFakeObjCopyTest(Map<String, String> config,
258
String debugExt,
259
String expectedObjCopy) throws Exception {
260
StripNativeDebugSymbolsPlugin plugin = createAndConfigPlugin(config, expectedObjCopy);
261
String sharedLib = "myLib.so";
262
String path = "/fib/lib/" + sharedLib;
263
ResourcePoolEntry debugEntry = createMockEntry(path,
264
ResourcePoolEntry.Type.NATIVE_LIB);
265
ResourcePoolManager inResources = new ResourcePoolManager();
266
ResourcePoolManager outResources = new ResourcePoolManager();
267
inResources.add(debugEntry);
268
ResourcePool output = plugin.transform(
269
inResources.resourcePool(),
270
outResources.resourcePoolBuilder());
271
// expect entry + debug info entry to be present
272
String debugPath = path + "." + debugExt;
273
if (output.findEntry(path).isPresent() &&
274
output.findEntry(debugPath).isPresent()) {
275
System.out.println("DEBUG: Files " + path + "{,." + debugExt +
276
"} present as exptected.");
277
} else {
278
throw new AssertionError("Test failed. Binary files " + path +
279
"{,." + debugExt +"} not present after " +
280
"stripping!");
281
}
282
verifyFakeObjCopyCalledMultiple(sharedLib, debugExt);
283
}
284
285
///////////////////////////////////////////////////////////////////////////
286
//
287
// Tests which DO rely on objcopy being present on the test system.
288
// Skipped otherwise.
289
//
290
///////////////////////////////////////////////////////////////////////////
291
292
public void testStripNativeLibraryDefaults() throws Exception {
293
if (!hasJmods()) return;
294
295
Path libFibJmod = createLibFibJmod();
296
297
Path imageDir = Paths.get("stripped-native-libs");
298
JLink.run("--output", imageDir.toString(),
299
"--verbose",
300
"--module-path", modulePathWith(libFibJmod),
301
"--add-modules", MODULE_NAME_WITH_NATIVE,
302
"--strip-native-debug-symbols=exclude-debuginfo-files").output();
303
Path libDir = imageDir.resolve("lib");
304
Path postStripLib = libDir.resolve(NATIVE_LIB_NAME);
305
long postStripSize = postStripLib.toFile().length();
306
307
if (postStripSize == 0) {
308
throw new AssertionError("Lib file size 0. Test error?!");
309
}
310
// Heuristic: libLib.so is smaller post debug info stripping
311
if (postStripSize >= ORIG_LIB_FIB_SIZE) {
312
throw new AssertionError("Expected native library stripping to " +
313
"reduce file size. Expected < " +
314
ORIG_LIB_FIB_SIZE + ", got: " + postStripSize);
315
} else {
316
System.out.println("DEBUG: File size of " + postStripLib.toString() +
317
" " + postStripSize + " < " + ORIG_LIB_FIB_SIZE + " as expected." );
318
}
319
verifyFibModule(imageDir); // Sanity check fib module which got libFib.so stripped
320
System.out.println("DEBUG: testStripNativeLibraryDefaults() PASSED!");
321
}
322
323
public void testOptionsInvalidObjcopy() throws Exception {
324
if (!hasJmods()) return;
325
326
Path libFibJmod = createLibFibJmod();
327
328
String notExists = "/do/not/exist/objcopy";
329
330
Path imageDir = Paths.get("invalid-objcopy-command");
331
String[] jlinkCmdArray = new String[] {
332
JAVA_HOME + File.separator + "bin" + File.separator + "jlink",
333
"--output", imageDir.toString(),
334
"--verbose",
335
"--module-path", modulePathWith(libFibJmod),
336
"--add-modules", MODULE_NAME_WITH_NATIVE,
337
"--strip-native-debug-symbols", "objcopy=" + notExists,
338
};
339
List<String> jlinkCmd = Arrays.asList(jlinkCmdArray);
340
System.out.println("Debug: command: " + jlinkCmd.stream().collect(
341
Collectors.joining(" ")));
342
ProcessBuilder builder = new ProcessBuilder(jlinkCmd);
343
Process p = builder.start();
344
int status = p.waitFor();
345
if (status == 0) {
346
throw new AssertionError("Expected jlink to fail!");
347
} else {
348
verifyInvalidObjcopyError(p.getInputStream(), notExists);
349
System.out.println("DEBUG: testOptionsInvalidObjcopy() PASSED!");
350
}
351
}
352
353
public void testStripNativeLibsDebugSymsIncluded() throws Exception {
354
if (!hasJmods()) return;
355
356
Path libFibJmod = createLibFibJmod();
357
358
Path imageDir = Paths.get("stripped-native-libs-with-debug");
359
JLink.run("--output", imageDir.toString(),
360
"--verbose",
361
"--module-path", modulePathWith(libFibJmod),
362
"--add-modules", MODULE_NAME_WITH_NATIVE,
363
"--strip-native-debug-symbols",
364
"keep-debuginfo-files=" + DEBUG_EXTENSION);
365
366
Path libDir = imageDir.resolve("lib");
367
Path postStripLib = libDir.resolve(NATIVE_LIB_NAME);
368
long postStripSize = postStripLib.toFile().length();
369
370
if (postStripSize == 0) {
371
throw new AssertionError("Lib file size 0. Test error?!");
372
}
373
// Heuristic: libLib.so is smaller post debug info stripping
374
if (postStripSize >= ORIG_LIB_FIB_SIZE) {
375
throw new AssertionError("Expected native library stripping to " +
376
"reduce file size. Expected < " +
377
ORIG_LIB_FIB_SIZE + ", got: " + postStripSize);
378
} else {
379
System.out.println("DEBUG: File size of " + postStripLib.toString() +
380
" " + postStripSize + " < " + ORIG_LIB_FIB_SIZE + " as expected." );
381
}
382
// stripped with option to preserve debug symbols file
383
verifyDebugInfoSymbolFilePresent(imageDir);
384
System.out.println("DEBUG: testStripNativeLibsDebugSymsIncluded() PASSED!");
385
}
386
387
private void verifyFakeObjCopyCalledMultiple(String expectedFile,
388
String dbgExt) throws Exception {
389
// transform of the StripNativeDebugSymbolsPlugin created objcopy.log
390
// with our stubbed FakeObjCopy. See FakeObjCopy.java
391
List<String> allLines = Files.readAllLines(Paths.get(FAKE_OBJ_COPY_LOG_FILE));
392
if (allLines.size() != 3) {
393
throw new AssertionError("Expected 3 calls to objcopy");
394
}
395
// 3 calls to objcopy are as follows:
396
// 1. Only keep debug symbols
397
// 2. Strip debug symbols
398
// 3. Add debug link to stripped file
399
String onlyKeepDebug = allLines.get(0);
400
String stripSymbolsLine = allLines.get(1);
401
String addGnuDebugLink = allLines.get(2);
402
System.out.println("DEBUG: Inspecting fake objcopy calls: " + allLines);
403
boolean passed = stripSymbolsLine.startsWith(OBJCOPY_ONLY_DEBUG_SYMS_OPT);
404
passed &= stripSymbolsLine.endsWith(expectedFile);
405
String[] tokens = onlyKeepDebug.split("\\s");
406
passed &= tokens[0].equals(OBJCOPY_ONLY_KEEP_DEBUG_SYMS_OPT);
407
passed &= tokens[1].endsWith(expectedFile);
408
passed &= tokens[2].endsWith(expectedFile + "." + dbgExt);
409
tokens = addGnuDebugLink.split("\\s");
410
String[] addDbgTokens = tokens[0].split("=");
411
passed &= addDbgTokens[1].equals(expectedFile + "." + dbgExt);
412
passed &= addDbgTokens[0].equals(OBJCOPY_ADD_DEBUG_LINK_OPT);
413
passed &= tokens[1].endsWith(expectedFile);
414
if (!passed) {
415
throw new AssertionError("Test failed! objcopy not properly called " +
416
"with expected options!");
417
}
418
}
419
420
private void verifyFakeObjCopyCalled(String expectedFile) throws Exception {
421
// transform of the StripNativeDebugSymbolsPlugin created objcopy.log
422
// with our stubbed FakeObjCopy. See FakeObjCopy.java
423
List<String> allLines = Files.readAllLines(Paths.get(FAKE_OBJ_COPY_LOG_FILE));
424
if (allLines.size() != 1) {
425
throw new AssertionError("Expected 1 call to objcopy only");
426
}
427
String optionLine = allLines.get(0);
428
System.out.println("DEBUG: Inspecting fake objcopy arguments: " + optionLine);
429
boolean passed = optionLine.startsWith(OBJCOPY_ONLY_DEBUG_SYMS_OPT);
430
passed &= optionLine.endsWith(expectedFile);
431
if (!passed) {
432
throw new AssertionError("Test failed! objcopy not called with " +
433
"expected options!");
434
}
435
}
436
437
private ResourcePoolEntry createMockEntry(String path,
438
ResourcePoolEntry.Type type) {
439
byte[] mockContent = new byte[] { 0, 1, 2, 3 };
440
ResourcePoolEntry entry = ResourcePoolEntry.create(
441
path,
442
type,
443
mockContent);
444
return entry;
445
}
446
447
private StripNativeDebugSymbolsPlugin createAndConfigPlugin(
448
Map<String, String> config,
449
String expectedObjcopy)
450
throws IOException {
451
TestObjCopyCmdBuilder cmdBuilder = new TestObjCopyCmdBuilder(expectedObjcopy);
452
return createAndConfigPlugin(config, cmdBuilder);
453
}
454
455
private StripNativeDebugSymbolsPlugin createAndConfigPlugin(
456
Map<String, String> config) throws IOException {
457
TestObjCopyCmdBuilder cmdBuilder = new TestObjCopyCmdBuilder();
458
return createAndConfigPlugin(config, cmdBuilder);
459
}
460
461
private StripNativeDebugSymbolsPlugin createAndConfigPlugin(
462
Map<String, String> config,
463
TestObjCopyCmdBuilder builder) throws IOException {
464
StripNativeDebugSymbolsPlugin plugin =
465
new StripNativeDebugSymbolsPlugin(builder);
466
plugin.doConfigure(false, config);
467
return plugin;
468
}
469
470
// Create the jmod with the native library
471
private Path createLibFibJmod() throws IOException {
472
JmodFileBuilder jmodBuilder = new JmodFileBuilder(MODULE_NAME_WITH_NATIVE);
473
jmodBuilder.javaClass(FIBJNI_JAVA_CLASS);
474
jmodBuilder.nativeLib(LIB_FIB_SRC);
475
return jmodBuilder.build();
476
}
477
478
private String modulePathWith(Path jmod) {
479
return Paths.get(JAVA_HOME, "jmods").toString() +
480
File.pathSeparator + jmod.getParent().toString();
481
}
482
483
private boolean hasJmods() {
484
if (!Files.exists(Paths.get(JAVA_HOME, "jmods"))) {
485
System.err.println("Test skipped. NO jmods directory");
486
return false;
487
}
488
return true;
489
}
490
491
private void verifyInvalidObjcopyError(InputStream errInput, String match) {
492
boolean foundMatch = false;
493
try (Scanner scanner = new Scanner(errInput)) {
494
while (scanner.hasNextLine()) {
495
String line = scanner.nextLine();
496
System.out.println("DEBUG: >>>> " + line);
497
if (line.contains(match)) {
498
foundMatch = true;
499
break;
500
}
501
}
502
}
503
if (!foundMatch) {
504
throw new AssertionError("Expected to find " + match +
505
" in error stream.");
506
} else {
507
System.out.println("DEBUG: Found string " + match + " as expected.");
508
}
509
}
510
511
private void verifyDebugInfoSymbolFilePresent(Path image)
512
throws IOException, InterruptedException {
513
Path debugSymsFile = image.resolve("lib/libFib.so.debug");
514
if (!Files.exists(debugSymsFile)) {
515
throw new AssertionError("Expected stripped debug info file " +
516
debugSymsFile.toString() + " to exist.");
517
}
518
long debugSymsSize = debugSymsFile.toFile().length();
519
if (debugSymsSize <= 0) {
520
throw new AssertionError("sanity check for fib.FibJNI failed " +
521
"post-stripping!");
522
} else {
523
System.out.println("DEBUG: Debug symbols stripped from libFib.so " +
524
"present (" + debugSymsFile.toString() + ") as expected.");
525
}
526
}
527
528
private void verifyFibModule(Path image)
529
throws IOException, InterruptedException {
530
System.out.println("DEBUG: sanity checking fib module...");
531
Path launcher = image.resolve("bin/java");
532
List<String> args = new ArrayList<>();
533
args.add(launcher.toString());
534
args.add("--add-modules");
535
args.add(MODULE_NAME_WITH_NATIVE);
536
args.add("fib.FibJNI");
537
args.add("7");
538
args.add("13"); // fib(7) == 13
539
System.out.println("DEBUG: [command] " +
540
args.stream().collect(Collectors.joining(" ")));
541
Process proc = new ProcessBuilder(args).inheritIO().start();
542
int status = proc.waitFor();
543
if (status == 0) {
544
System.out.println("DEBUG: sanity checking fib module... PASSED!");
545
} else {
546
throw new AssertionError("sanity check for fib.FibJNI failed post-" +
547
"stripping!");
548
}
549
}
550
551
private static boolean isObjcopyPresent() throws Exception {
552
String[] objcopyVersion = new String[] {
553
OBJCOPY, "--version",
554
};
555
List<String> command = Arrays.asList(objcopyVersion);
556
try {
557
ProcessBuilder builder = new ProcessBuilder(command);
558
builder.inheritIO();
559
Process p = builder.start();
560
int status = p.waitFor();
561
if (status != 0) {
562
System.out.println("Debug: objcopy binary doesn't seem to be " +
563
"present or functional.");
564
return false;
565
}
566
} catch (IOException e) {
567
System.out.println("Debug: objcopy binary doesn't seem to be present " +
568
"or functional.");
569
return false;
570
}
571
return true;
572
}
573
574
public static void main(String[] args) throws Exception {
575
StripNativeDebugSymbolsPluginTest test = new StripNativeDebugSymbolsPluginTest();
576
if (isObjcopyPresent()) {
577
test.testStripNativeLibraryDefaults();
578
test.testStripNativeLibsDebugSymsIncluded();
579
test.testOptionsInvalidObjcopy();
580
} else {
581
System.out.println("DEBUG: objcopy binary not available. " +
582
"Running reduced set of tests.");
583
}
584
test.testTransformFakeObjCopyNoDebugInfoFiles();
585
test.testTransformFakeObjCopyKeepDebugInfoFiles();
586
test.testConfigureFakeObjCopy();
587
test.testPluginLoaded();
588
}
589
590
static class JLink {
591
static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
592
.orElseThrow(() ->
593
new RuntimeException("jlink tool not found")
594
);
595
596
static JLink run(String... options) {
597
JLink jlink = new JLink();
598
if (jlink.execute(options) != 0) {
599
throw new AssertionError("Jlink expected to exit with 0 return code");
600
}
601
return jlink;
602
}
603
604
final List<String> output = new ArrayList<>();
605
private int execute(String... options) {
606
System.out.println("jlink " +
607
Stream.of(options).collect(Collectors.joining(" ")));
608
609
StringWriter writer = new StringWriter();
610
PrintWriter pw = new PrintWriter(writer);
611
int rc = JLINK_TOOL.run(pw, pw, options);
612
System.out.println(writer.toString());
613
Stream.of(writer.toString().split("\\v"))
614
.map(String::trim)
615
.forEach(output::add);
616
return rc;
617
}
618
619
boolean contains(String s) {
620
return output.contains(s);
621
}
622
623
List<String> output() {
624
return output;
625
}
626
}
627
628
/**
629
* Builder to create JMOD file
630
*/
631
private static class JmodFileBuilder {
632
633
private static final ToolProvider JMOD_TOOL = ToolProvider
634
.findFirst("jmod")
635
.orElseThrow(() ->
636
new RuntimeException("jmod tool not found")
637
);
638
private static final Path SRC_DIR = Paths.get("src");
639
private static final Path MODS_DIR = Paths.get("mod");
640
private static final Path JMODS_DIR = Paths.get("jmods");
641
private static final Path LIBS_DIR = Paths.get("libs");
642
643
private final String name;
644
private final List<Path> nativeLibs = new ArrayList<>();
645
private final List<Path> javaClasses = new ArrayList<>();
646
647
private JmodFileBuilder(String name) throws IOException {
648
this.name = name;
649
650
deleteDirectory(MODS_DIR);
651
deleteDirectory(SRC_DIR);
652
deleteDirectory(LIBS_DIR);
653
deleteDirectory(JMODS_DIR);
654
Path msrc = SRC_DIR.resolve(name);
655
if (Files.exists(msrc)) {
656
deleteDirectory(msrc);
657
}
658
}
659
660
JmodFileBuilder nativeLib(Path libFileSrc) {
661
nativeLibs.add(libFileSrc);
662
return this;
663
}
664
665
JmodFileBuilder javaClass(Path srcPath) {
666
javaClasses.add(srcPath);
667
return this;
668
}
669
670
Path build() throws IOException {
671
compileModule();
672
return createJmodFile();
673
}
674
675
private void compileModule() throws IOException {
676
Path msrc = SRC_DIR.resolve(name);
677
Files.createDirectories(msrc);
678
// copy class using native lib to expected path
679
if (javaClasses.size() > 0) {
680
for (Path srcPath: javaClasses) {
681
Path targetPath = msrc.resolve(srcPath.getFileName());
682
Files.copy(srcPath, targetPath);
683
}
684
}
685
// generate module-info file.
686
Path minfo = msrc.resolve("module-info.java");
687
try (BufferedWriter bw = Files.newBufferedWriter(minfo);
688
PrintWriter writer = new PrintWriter(bw)) {
689
writer.format("module %s { }%n", name);
690
}
691
692
if (!CompilerUtils.compile(msrc, MODS_DIR,
693
"--module-source-path",
694
SRC_DIR.toString())) {
695
696
}
697
}
698
699
private Path createJmodFile() throws IOException {
700
Path mclasses = MODS_DIR.resolve(name);
701
Files.createDirectories(JMODS_DIR);
702
Path outfile = JMODS_DIR.resolve(name + ".jmod");
703
List<String> args = new ArrayList<>();
704
args.add("create");
705
// add classes
706
args.add("--class-path");
707
args.add(mclasses.toString());
708
// native libs
709
if (nativeLibs.size() > 0) {
710
// Copy the JNI library to the expected path
711
Files.createDirectories(LIBS_DIR);
712
for (Path srcLib: nativeLibs) {
713
Path targetLib = LIBS_DIR.resolve(srcLib.getFileName());
714
Files.copy(srcLib, targetLib);
715
}
716
args.add("--libs");
717
args.add(LIBS_DIR.toString());
718
}
719
args.add(outfile.toString());
720
721
if (Files.exists(outfile)) {
722
Files.delete(outfile);
723
}
724
725
System.out.println("jmod " +
726
args.stream().collect(Collectors.joining(" ")));
727
728
int rc = JMOD_TOOL.run(System.out, System.out,
729
args.toArray(new String[args.size()]));
730
if (rc != 0) {
731
throw new AssertionError("jmod failed: rc = " + rc);
732
}
733
return outfile;
734
}
735
736
private static void deleteDirectory(Path dir) throws IOException {
737
try {
738
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
739
@Override
740
public FileVisitResult visitFile(Path file,
741
BasicFileAttributes attrs)
742
throws IOException
743
{
744
Files.delete(file);
745
return FileVisitResult.CONTINUE;
746
}
747
748
@Override
749
public FileVisitResult postVisitDirectory(Path dir,
750
IOException exc)
751
throws IOException
752
{
753
Files.delete(dir);
754
return FileVisitResult.CONTINUE;
755
}
756
});
757
} catch (NoSuchFileException e) {
758
// ignore non-existing files
759
}
760
}
761
}
762
763
private static class TestObjCopyCmdBuilder implements ObjCopyCmdBuilder {
764
765
private final String expectedObjCopy;
766
private final String logFile;
767
768
TestObjCopyCmdBuilder() {
769
this(DEFAULT_OBJCOPY_CMD);
770
}
771
TestObjCopyCmdBuilder(String exptectedObjCopy) {
772
Path logFilePath = Paths.get(FAKE_OBJ_COPY_LOG_FILE);
773
try {
774
Files.deleteIfExists(logFilePath);
775
} catch (Exception e) {
776
e.printStackTrace();
777
}
778
this.logFile = logFilePath.toFile().getAbsolutePath();
779
this.expectedObjCopy = exptectedObjCopy;
780
}
781
782
@Override
783
public List<String> build(String objCopy, String... options) {
784
if (!expectedObjCopy.equals(objCopy)) {
785
throw new AssertionError("Expected objcopy to be '" +
786
expectedObjCopy + "' but was '" +
787
objCopy);
788
}
789
List<String> fakeObjCopy = new ArrayList<>();
790
fakeObjCopy.add(JAVA_HOME + File.separator + "bin" + File.separator + "java");
791
fakeObjCopy.add("-cp");
792
fakeObjCopy.add(System.getProperty("test.classes"));
793
fakeObjCopy.add("FakeObjCopy");
794
// Note that adding the gnu debug link changes the PWD of the
795
// java process calling FakeObjCopy. As such we need to pass in the
796
// log file path this way. Relative paths won't work as it would be
797
// relative to the temporary directory which gets deleted post
798
// adding the debug link
799
fakeObjCopy.add(logFile);
800
if (options.length > 0) {
801
fakeObjCopy.addAll(Arrays.asList(options));
802
}
803
return fakeObjCopy;
804
}
805
806
}
807
}
808
809