1
1
/*
2
- * Copyright (c) 2019, 2021 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2019, 2025 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
24
24
*/
25
25
package com .oracle .svm .agent ;
26
26
27
+ import static com .oracle .svm .agent .NativeImageAgent .ExitCodes .AGENT_ERROR ;
28
+ import static com .oracle .svm .agent .NativeImageAgent .ExitCodes .PARSE_ERROR ;
29
+ import static com .oracle .svm .agent .NativeImageAgent .ExitCodes .SUCCESS ;
30
+ import static com .oracle .svm .agent .NativeImageAgent .ExitCodes .USAGE_ERROR ;
31
+
27
32
import java .io .FileNotFoundException ;
28
33
import java .io .IOException ;
29
34
import java .nio .file .AtomicMoveNotSupportedException ;
@@ -154,12 +159,12 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
154
159
for (String token : tokens ) {
155
160
if (token .startsWith ("trace-output=" )) {
156
161
if (traceOutputFile != null ) {
157
- return usage (1 , "cannot specify trace-output= more than once." );
162
+ return usage ("cannot specify trace-output= more than once." );
158
163
}
159
164
traceOutputFile = getTokenValue (token );
160
165
} else if (token .startsWith ("config-output-dir=" ) || token .startsWith ("config-merge-dir=" )) {
161
166
if (configOutputDir != null ) {
162
- return usage (1 , "cannot specify more than one of config-output-dir= or config-merge-dir=." );
167
+ return usage ("cannot specify more than one of config-output-dir= or config-merge-dir=." );
163
168
}
164
169
configOutputDir = transformPath (getTokenValue (token ));
165
170
if (token .startsWith ("config-merge-dir=" )) {
@@ -197,12 +202,12 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
197
202
} else if (token .startsWith ("config-write-period-secs=" )) {
198
203
configWritePeriod = parseIntegerOrNegative (getTokenValue (token ));
199
204
if (configWritePeriod <= 0 ) {
200
- return usage (1 , "config-write-period-secs must be an integer greater than 0" );
205
+ return usage ("config-write-period-secs must be an integer greater than 0" );
201
206
}
202
207
} else if (token .startsWith ("config-write-initial-delay-secs=" )) {
203
208
configWritePeriodInitialDelay = parseIntegerOrNegative (getTokenValue (token ));
204
209
if (configWritePeriodInitialDelay < 0 ) {
205
- return usage (1 , "config-write-initial-delay-secs must be an integer greater or equal to 0" );
210
+ return usage ("config-write-initial-delay-secs must be an integer greater or equal to 0" );
206
211
}
207
212
} else if (isBooleanOption (token , "experimental-configuration-with-origins" )) {
208
213
configurationWithOrigins = getBooleanTokenValue (token );
@@ -215,7 +220,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
215
220
} else if (isBooleanOption (token , "track-reflection-metadata" )) {
216
221
trackReflectionMetadata = getBooleanTokenValue (token );
217
222
} else {
218
- return usage (1 , "unknown option: '" + token + "'." );
223
+ return usage ("unknown option: '" + token + "'." );
219
224
}
220
225
}
221
226
@@ -225,7 +230,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
225
230
}
226
231
227
232
if (configurationWithOrigins && !conditionalConfigUserPackageFilterFiles .isEmpty ()) {
228
- return error (5 , "The agent can only be used in either the configuration with origins mode or the predefined classes mode." );
233
+ return error (USAGE_ERROR , "The agent can only be used in either the configuration with origins mode or the predefined classes mode." );
229
234
}
230
235
231
236
if (configurationWithOrigins && !mergeConfigs .isEmpty ()) {
@@ -250,20 +255,20 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
250
255
callerFilter = new ComplexFilter (callerFilterHierarchyFilterNode );
251
256
}
252
257
if (!parseFilterFiles (callerFilter , callerFilterFiles )) {
253
- return 1 ;
258
+ return PARSE_ERROR ;
254
259
}
255
260
}
256
261
257
262
ComplexFilter accessFilter = null ;
258
263
if (!accessFilterFiles .isEmpty ()) {
259
264
accessFilter = new ComplexFilter (AccessAdvisor .copyBuiltinAccessFilterTree ());
260
265
if (!parseFilterFiles (accessFilter , accessFilterFiles )) {
261
- return 1 ;
266
+ return PARSE_ERROR ;
262
267
}
263
268
}
264
269
265
270
if (!conditionalConfigUserPackageFilterFiles .isEmpty () && conditionalConfigPartialRun ) {
266
- return error (6 , "The agent can generate conditional configuration either for the current run or in the partial mode but not both at the same time." );
271
+ return error (USAGE_ERROR , "The agent can generate conditional configuration either for the current run or in the partial mode but not both at the same time." );
267
272
}
268
273
269
274
boolean isConditionalConfigurationRun = !conditionalConfigUserPackageFilterFiles .isEmpty () || conditionalConfigPartialRun ;
@@ -274,7 +279,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
274
279
275
280
if (configOutputDir != null ) {
276
281
if (traceOutputFile != null ) {
277
- return usage (1 , "can only once specify exactly one of trace-output=, config-output-dir= or config-merge-dir=." );
282
+ return usage ("can only once specify exactly one of trace-output=, config-output-dir= or config-merge-dir=." );
278
283
}
279
284
try {
280
285
configOutputDirPath = Files .createDirectories (Path .of (configOutputDir ));
@@ -289,7 +294,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
289
294
} catch (Exception ignored ) {
290
295
process = "(unknown)" ;
291
296
}
292
- return error (2 , "Output directory '" + configOutputDirPath + "' is locked by process " + process + ", " +
297
+ return error (AGENT_ERROR , "Output directory '" + configOutputDirPath + "' is locked by process " + process + ", " +
293
298
"which means another agent instance is already writing to this directory. " +
294
299
"Only one agent instance can safely write to a specific target directory at the same time. " +
295
300
"Unless file '" + ConfigurationFile .LOCK_FILE_NAME + "' is a leftover from an earlier process that terminated abruptly, it is unsafe to delete it. " +
@@ -322,13 +327,13 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
322
327
} else {
323
328
ComplexFilter userCodeFilter = new ComplexFilter (HierarchyFilterNode .createRoot ());
324
329
if (!parseFilterFiles (userCodeFilter , conditionalConfigUserPackageFilterFiles )) {
325
- return 2 ;
330
+ return PARSE_ERROR ;
326
331
}
327
332
ComplexFilter classNameFilter ;
328
333
if (!conditionalConfigClassNameFilterFiles .isEmpty ()) {
329
334
classNameFilter = new ComplexFilter (HierarchyFilterNode .createRoot ());
330
335
if (!parseFilterFiles (classNameFilter , conditionalConfigClassNameFilterFiles )) {
331
- return 3 ;
336
+ return PARSE_ERROR ;
332
337
}
333
338
} else {
334
339
classNameFilter = new ComplexFilter (HierarchyFilterNode .createInclusiveRoot ());
@@ -360,16 +365,16 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
360
365
}
361
366
expectedConfigModifiedBefore = getMostRecentlyModified (configOutputDirPath , getMostRecentlyModified (configOutputLockFilePath , null ));
362
367
} catch (Throwable t ) {
363
- return error (2 , t .toString ());
368
+ return error (AGENT_ERROR , t .toString ());
364
369
}
365
- } else if ( traceOutputFile != null ) {
370
+ } else {
366
371
try {
367
372
Path path = Paths .get (transformPath (traceOutputFile ));
368
373
TraceFileWriter writer = new TraceFileWriter (path );
369
374
tracer = writer ;
370
375
tracingResultWriter = writer ;
371
376
} catch (Throwable t ) {
372
- return error (2 , t .toString ());
377
+ return error (AGENT_ERROR , t .toString ());
373
378
}
374
379
}
375
380
@@ -381,16 +386,16 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
381
386
BreakpointInterceptor .onLoad (jvmti , callbacks , tracer , this , interceptedStateSupplier ,
382
387
experimentalClassLoaderSupport , experimentalClassDefineSupport , experimentalUnsafeAllocationSupport , trackReflectionMetadata );
383
388
} catch (Throwable t ) {
384
- return error (3 , t .toString ());
389
+ return error (AGENT_ERROR , t .toString ());
385
390
}
386
391
try {
387
392
JniCallInterceptor .onLoad (tracer , this , interceptedStateSupplier );
388
393
} catch (Throwable t ) {
389
- return error (4 , t .toString ());
394
+ return error (AGENT_ERROR , t .toString ());
390
395
}
391
396
392
397
setupExecutorServiceForPeriodicConfigurationCapture (configWritePeriod , configWritePeriodInitialDelay );
393
- return 0 ;
398
+ return SUCCESS ;
394
399
}
395
400
396
401
private static void inform (String message ) {
@@ -408,11 +413,11 @@ private static <T> T error(T result, String message) {
408
413
return result ;
409
414
}
410
415
411
- private static < T > T usage (T result , String message ) {
416
+ private static int usage (String message ) {
412
417
inform (message );
413
418
inform ("Example usage: -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/" );
414
419
inform ("For details, please read AutomaticMetadataCollection.md or https://www.graalvm.org/dev/reference-manual/native-image/metadata/AutomaticMetadataCollection/" );
415
- return result ;
420
+ return USAGE_ERROR ;
416
421
}
417
422
418
423
private static AccessAdvisor createAccessAdvisor (boolean builtinHeuristicFilter , ConfigurationFilter callerFilter , ConfigurationFilter accessFilter ) {
@@ -689,7 +694,14 @@ protected int onUnloadCallback(JNIJavaVM vm) {
689
694
* The epilogue of this method does not tear down our VM: we don't seem to observe all
690
695
* threads that end and therefore can't detach them, so we would wait forever for them.
691
696
*/
692
- return 0 ;
697
+ return SUCCESS ;
698
+ }
699
+
700
+ static class ExitCodes {
701
+ static final int SUCCESS = 0 ;
702
+ static final int USAGE_ERROR = 1 ;
703
+ static final int PARSE_ERROR = 2 ;
704
+ static final int AGENT_ERROR = 3 ;
693
705
}
694
706
695
707
@ SuppressWarnings ("unused" )
0 commit comments