Re: RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors

2018-07-09 Thread Peter Levart

Hi David,

On 07/09/2018 09:33 AM, David Holmes wrote:

On 9/07/2018 5:22 PM, Peter Levart wrote:

Hi David,

On 07/09/2018 03:37 AM, David Holmes wrote:

Hi Peter,

On 7/07/2018 2:10 AM, Peter Levart wrote:

Hi,

On 07/05/2018 01:01 AM, David Holmes wrote:
I dispute "they will understand this might have happened in 
another thread". 


What if the stack trace was like the following...


Yes your suggestion makes it much clearer.

But ... my whole objection here is doing all this extraneous 
execution of Java code in response to the initial exception. The 
more Java code we execute the more likely we will hit secondary 
exceptions and the greater the possibility of unintended 
interactions that might lead back to the class that can't be 
initialized. I just don't think this level of effort is warranted. I


I agree that more classes are involved, but they are all JDK classes 
and their number is constant. Meaning, if they are OK and don't fail 
when initializing, there's no danger of unintended interactions that 
would be caused by initialization errors in other classes. And even 
if those additional needed classes had problems in their 
initialization, I think that the consequences would be under control.


That's not what I mean. I'm not concerned about circular 
initialization failures due to failing to initialize the classes used 
in this "hook". I'm concerned about the overall amount of Java code 
execution that this involves, which may trigger other exceptions (e.g. 
OOME) and which may incur additional logging or event generation that 
may in turn interact in some way with the original class being 
initialized.




I still can't see what you see. If java code that records initial 
initialization error throws OOME, it will be ignored and initial 
exception will not be recorded. Later NoClassDefFoundError would not 
contain the nice chain of causes, but that's the only undesirable 
consequence. The code doesn't log OOME, it simply ignores it. If those 
additional exceptions cause any events on the VM level and those events 
interact in some way with the original class being initialized, this 
interaction will fail, but such interaction with original class would 
fail even if it was caused by anything else, because it is the original 
class that had problems initializing itself in the 1st place.


I just think this is complete overkill for addressing the perceived 
problem.


Perhaps. But it would be nice to have.

Regards, Peter



David
-



Let's see what additional classes are needed when the presented patch 
is used as opposed to classes needed in current logic:


In step 7, when super class/interface initialization fails and in 
steps 10/11 when the class initialization fails, we record the error 
thrown (record_init_exception). In addition to previously needed 
classes we also need:

- ClassLoader,
- ClassLoader$InitExceptionSubst (with dependencies: 
RuntimeException, Exception),
- ClassLoaderValue (with dependencies: AbstractClassLoaderValue, 
AbstractClassLoaderValue$Sub, AbstractClassLoaderValue$Memoizer, 
ConcurrentHashMap + deps)


When we throw NoClassDefFoundError, we don't need any other 
additional classes that wouldn't already be needed originally.


So I can see that when above additional classes had problems 
initializing themselves, there would be errors thrown from their 
usage when recording initial initialization exception of some 
unrelated class, but such errors would be ignored (step 7):


  979 // Record the exception thrown from super 
class/interface initialization so that
  980 // it can be chained into potential later 
NoClassDefFoundErrors.
  981 
class_loader_data()->record_init_exception(java_mirror_handle(), e, 
THREAD);

  982 // Locks object, set state, and notify all waiting threads
  983 set_initialization_state_and_notify(initialization_error, THREAD);
  984 *CLEAR_PENDING_EXCEPTION*;

steps 10/11:

1037   // Record the exception that originally caused  to 
fail so
1038   // it can be chained into potential later 
NoClassDefFoundErrors.
1039 class_loader_data()->record_init_exception(java_mirror_handle(), 
e, THREAD);

1040   // Locks object, set state, and notify all waiting threads
1041 set_initialization_state_and_notify(initialization_error, THREAD);
1042 *CLEAR_PENDING_EXCEPTION*;


It might be that those ignored exceptions would cause later use of 
those additional classes to throw NoClassDefFoundError instead of 
ExceptionInInitializerError (depending on whether it was an initial 
attempt to initialize those additional classes or not), but I can't 
see any other undesirable consequence. Do you?


I'll try to provoke initialization errors in those additional classes 
to see what happens. Will get back when I have results of the 
experiment...


Regards, Peter


P.S.

Executing java code as part of VM logic plays well in Jigsaw for 
example. If there is an acceptable fallback in case of java logic 
failure, everythin

Re: RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors

2018-07-09 Thread David Holmes

On 9/07/2018 5:22 PM, Peter Levart wrote:

Hi David,

On 07/09/2018 03:37 AM, David Holmes wrote:

Hi Peter,

On 7/07/2018 2:10 AM, Peter Levart wrote:

Hi,

On 07/05/2018 01:01 AM, David Holmes wrote:
I dispute "they will understand this might have happened in another 
thread". 


What if the stack trace was like the following...


Yes your suggestion makes it much clearer.

But ... my whole objection here is doing all this extraneous execution 
of Java code in response to the initial exception. The more Java code 
we execute the more likely we will hit secondary exceptions and the 
greater the possibility of unintended interactions that might lead 
back to the class that can't be initialized. I just don't think this 
level of effort is warranted. I


I agree that more classes are involved, but they are all JDK classes and 
their number is constant. Meaning, if they are OK and don't fail when 
initializing, there's no danger of unintended interactions that would be 
caused by initialization errors in other classes. And even if those 
additional needed classes had problems in their initialization, I think 
that the consequences would be under control.


That's not what I mean. I'm not concerned about circular initialization 
failures due to failing to initialize the classes used in this "hook". 
I'm concerned about the overall amount of Java code execution that this 
involves, which may trigger other exceptions (e.g. OOME) and which may 
incur additional logging or event generation that may in turn interact 
in some way with the original class being initialized.


I just think this is complete overkill for addressing the perceived problem.

David
-



Let's see what additional classes are needed when the presented patch is 
used as opposed to classes needed in current logic:


In step 7, when super class/interface initialization fails and in steps 
10/11 when the class initialization fails, we record the error thrown 
(record_init_exception). In addition to previously needed classes we 
also need:

- ClassLoader,
- ClassLoader$InitExceptionSubst (with dependencies: RuntimeException, 
Exception),
- ClassLoaderValue (with dependencies: AbstractClassLoaderValue, 
AbstractClassLoaderValue$Sub, AbstractClassLoaderValue$Memoizer, 
ConcurrentHashMap + deps)


When we throw NoClassDefFoundError, we don't need any other additional 
classes that wouldn't already be needed originally.


So I can see that when above additional classes had problems 
initializing themselves, there would be errors thrown from their usage 
when recording initial initialization exception of some unrelated class, 
but such errors would be ignored (step 7):


  979 // Record the exception thrown from super class/interface 
initialization so that
  980 // it can be chained into potential later 
NoClassDefFoundErrors.
  981 class_loader_data()->record_init_exception(java_mirror_handle(), 
e, THREAD);

  982 // Locks object, set state, and notify all waiting threads
  983 set_initialization_state_and_notify(initialization_error, THREAD);
  984 *CLEAR_PENDING_EXCEPTION*;

steps 10/11:

1037   // Record the exception that originally caused  to 
fail so

1038   // it can be chained into potential later NoClassDefFoundErrors.
1039 class_loader_data()->record_init_exception(java_mirror_handle(), e, 
THREAD);

1040   // Locks object, set state, and notify all waiting threads
1041   set_initialization_state_and_notify(initialization_error, 
THREAD);

1042 *CLEAR_PENDING_EXCEPTION*;


It might be that those ignored exceptions would cause later use of those 
additional classes to throw NoClassDefFoundError instead of 
ExceptionInInitializerError (depending on whether it was an initial 
attempt to initialize those additional classes or not), but I can't see 
any other undesirable consequence. Do you?


I'll try to provoke initialization errors in those additional classes to 
see what happens. Will get back when I have results of the experiment...


Regards, Peter


P.S.

Executing java code as part of VM logic plays well in Jigsaw for 
example. If there is an acceptable fallback in case of java logic 
failure, everything seems to be OK.



Cheers,
David
-


Before patch:

1st attempt [ForkJoinPool.commonPool-worker-3]:

java.lang.ExceptionInInitializerError
 at ClinitFailure.lambda$main$0(ClinitFailure.java:20)
 at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736) 

 at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728) 

 at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
 at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) 

 at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
 at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) 

 at 

Re: RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors

2018-07-09 Thread Peter Levart

Hi David,

On 07/09/2018 03:37 AM, David Holmes wrote:

Hi Peter,

On 7/07/2018 2:10 AM, Peter Levart wrote:

Hi,

On 07/05/2018 01:01 AM, David Holmes wrote:
I dispute "they will understand this might have happened in another 
thread". 


What if the stack trace was like the following...


Yes your suggestion makes it much clearer.

But ... my whole objection here is doing all this extraneous execution 
of Java code in response to the initial exception. The more Java code 
we execute the more likely we will hit secondary exceptions and the 
greater the possibility of unintended interactions that might lead 
back to the class that can't be initialized. I just don't think this 
level of effort is warranted. I


I agree that more classes are involved, but they are all JDK classes and 
their number is constant. Meaning, if they are OK and don't fail when 
initializing, there's no danger of unintended interactions that would be 
caused by initialization errors in other classes. And even if those 
additional needed classes had problems in their initialization, I think 
that the consequences would be under control.


Let's see what additional classes are needed when the presented patch is 
used as opposed to classes needed in current logic:


In step 7, when super class/interface initialization fails and in steps 
10/11 when the class initialization fails, we record the error thrown 
(record_init_exception). In addition to previously needed classes we 
also need:

- ClassLoader,
- ClassLoader$InitExceptionSubst (with dependencies: RuntimeException, 
Exception),
- ClassLoaderValue (with dependencies: AbstractClassLoaderValue, 
AbstractClassLoaderValue$Sub, AbstractClassLoaderValue$Memoizer, 
ConcurrentHashMap + deps)


When we throw NoClassDefFoundError, we don't need any other additional 
classes that wouldn't already be needed originally.


So I can see that when above additional classes had problems 
initializing themselves, there would be errors thrown from their usage 
when recording initial initialization exception of some unrelated class, 
but such errors would be ignored (step 7):


 979 // Record the exception thrown from super class/interface 
initialization so that
 980 // it can be chained into potential later 
NoClassDefFoundErrors.
 981 class_loader_data()->record_init_exception(java_mirror_handle(), 
e, THREAD);

 982 // Locks object, set state, and notify all waiting threads
 983 set_initialization_state_and_notify(initialization_error, THREAD);
 984 *CLEAR_PENDING_EXCEPTION*;

steps 10/11:

1037   // Record the exception that originally caused  to 
fail so

1038   // it can be chained into potential later NoClassDefFoundErrors.
1039 class_loader_data()->record_init_exception(java_mirror_handle(), e, 
THREAD);

1040   // Locks object, set state, and notify all waiting threads
1041   set_initialization_state_and_notify(initialization_error, 
THREAD);

1042 *CLEAR_PENDING_EXCEPTION*;


It might be that those ignored exceptions would cause later use of those 
additional classes to throw NoClassDefFoundError instead of 
ExceptionInInitializerError (depending on whether it was an initial 
attempt to initialize those additional classes or not), but I can't see 
any other undesirable consequence. Do you?


I'll try to provoke initialization errors in those additional classes to 
see what happens. Will get back when I have results of the experiment...


Regards, Peter


P.S.

Executing java code as part of VM logic plays well in Jigsaw for 
example. If there is an acceptable fallback in case of java logic 
failure, everything seems to be OK.



Cheers,
David
-


Before patch:

1st attempt [ForkJoinPool.commonPool-worker-3]:

java.lang.ExceptionInInitializerError
 at ClinitFailure.lambda$main$0(ClinitFailure.java:20)
 at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736) 

 at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728) 

 at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
 at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) 

 at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
 at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) 

 at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177) 


Caused by: java.lang.RuntimeException: Can't get it!
 at ClinitFailure$Faulty.(ClinitFailure.java:12)
 ... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of 
bounds for length 0

 at ClinitFailure$Faulty.(ClinitFailure.java:10)
 ... 8 more

2nd attempt [ForkJoinPool.commonPool-worker-5]:

java.lang.NoClassDefFoundError: Could not initialize class 
ClinitFailure$Faulty

 at ClinitFailure.lambda$main$1(Cl

Re: RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors

2018-07-08 Thread David Holmes

Hi Peter,

On 7/07/2018 2:10 AM, Peter Levart wrote:

Hi,

On 07/05/2018 01:01 AM, David Holmes wrote:
I dispute "they will understand this might have happened in another 
thread". 


What if the stack trace was like the following...


Yes your suggestion makes it much clearer.

But ... my whole objection here is doing all this extraneous execution 
of Java code in response to the initial exception. The more Java code we 
execute the more likely we will hit secondary exceptions and the greater 
the possibility of unintended interactions that might lead back to the 
class that can't be initialized. I just don't think this level of effort 
is warranted.


Cheers,
David
-


Before patch:

1st attempt [ForkJoinPool.commonPool-worker-3]:

java.lang.ExceptionInInitializerError
     at ClinitFailure.lambda$main$0(ClinitFailure.java:20)
     at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736) 

     at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728) 

     at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
     at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) 

     at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
     at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) 

     at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177) 


Caused by: java.lang.RuntimeException: Can't get it!
     at ClinitFailure$Faulty.(ClinitFailure.java:12)
     ... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of 
bounds for length 0

     at ClinitFailure$Faulty.(ClinitFailure.java:10)
     ... 8 more

2nd attempt [ForkJoinPool.commonPool-worker-5]:

java.lang.NoClassDefFoundError: Could not initialize class 
ClinitFailure$Faulty

     at ClinitFailure.lambda$main$1(ClinitFailure.java:28)
     at 
java.base/java.util.concurrent.CompletableFuture$UniRun.tryFire(CompletableFuture.java:783) 

     at 
java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:479) 

     at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
     at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) 

     at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
     at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) 

     at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177) 




After patch:

1st attempt [ForkJoinPool.commonPool-worker-3]:

java.lang.ExceptionInInitializerError
     at ClinitFailure.lambda$main$0(ClinitFailure.java:18)
     at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736) 

     at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728) 

     at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
     at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) 

     at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
     at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) 

     at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177) 


Caused by: java.lang.RuntimeException: Can't get it!
     at ClinitFailure$Faulty.(ClinitFailure.java:10)
     ... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of 
bounds for length 0

     at ClinitFailure$Faulty.(ClinitFailure.java:8)
     ... 8 more

2nd attempt [ForkJoinPool.commonPool-worker-5]:

java.lang.NoClassDefFoundError: Could not initialize class 
ClinitFailure$Faulty
     at 
java.base/java.lang.ClassLoader.throwReinitException(ClassLoader.java:3062)

     at ClinitFailure.lambda$main$1(ClinitFailure.java:25)
     at 
java.base/java.util.concurrent.CompletableFuture$UniRun.tryFire(CompletableFuture.java:783) 

     at 
java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:479) 

     at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
     at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) 

     at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
     at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) 

     at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177) 

Caused by: java.lang.ExceptionInInitializerError: 11 ms ago in thread 
ForkJoinPool.commonPool-worker-3

     at ClinitFailure.lambda$main$0(ClinitFailu

Re: RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors

2018-07-06 Thread Peter Levart

Hi,

On 07/05/2018 01:01 AM, David Holmes wrote:
I dispute "they will understand this might have happened in another 
thread". 


What if the stack trace was like the following...

Before patch:

1st attempt [ForkJoinPool.commonPool-worker-3]:

java.lang.ExceptionInInitializerError
    at ClinitFailure.lambda$main$0(ClinitFailure.java:20)
    at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
    at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
    at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)

Caused by: java.lang.RuntimeException: Can't get it!
    at ClinitFailure$Faulty.(ClinitFailure.java:12)
    ... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of 
bounds for length 0

    at ClinitFailure$Faulty.(ClinitFailure.java:10)
    ... 8 more

2nd attempt [ForkJoinPool.commonPool-worker-5]:

java.lang.NoClassDefFoundError: Could not initialize class 
ClinitFailure$Faulty

    at ClinitFailure.lambda$main$1(ClinitFailure.java:28)
    at 
java.base/java.util.concurrent.CompletableFuture$UniRun.tryFire(CompletableFuture.java:783)
    at 
java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:479)
    at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)



After patch:

1st attempt [ForkJoinPool.commonPool-worker-3]:

java.lang.ExceptionInInitializerError
    at ClinitFailure.lambda$main$0(ClinitFailure.java:18)
    at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
    at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
    at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)

Caused by: java.lang.RuntimeException: Can't get it!
    at ClinitFailure$Faulty.(ClinitFailure.java:10)
    ... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of 
bounds for length 0

    at ClinitFailure$Faulty.(ClinitFailure.java:8)
    ... 8 more

2nd attempt [ForkJoinPool.commonPool-worker-5]:

java.lang.NoClassDefFoundError: Could not initialize class 
ClinitFailure$Faulty
    at 
java.base/java.lang.ClassLoader.throwReinitException(ClassLoader.java:3062)

    at ClinitFailure.lambda$main$1(ClinitFailure.java:25)
    at 
java.base/java.util.concurrent.CompletableFuture$UniRun.tryFire(CompletableFuture.java:783)
    at 
java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:479)
    at 
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at 
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at 
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at 
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at 
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Caused by: java.lang.ExceptionInInitializerError: 11 ms ago in thread 
ForkJoinPool.commonPool-worker-3

    at ClinitFailure.lambda$main$0(ClinitFailure.java:18)
    at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
    at 
java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)

    ... 5 more
Caused by: java.lang.RuntimeException: Can't get it!
    at ClinitFailure$Faulty.(ClinitFailure.java:10)
    ... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of 
bounds for length 0

    at ClinitFailure$Faulty.(ClinitFailure.java:8)
    ... 8 more



This is what gets printed by the sample program:

p

Re: RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors

2018-07-04 Thread David Holmes

On 4/07/2018 6:21 PM, Lindenmaier, Goetz wrote:

Hi,

Volker, thanks for improving on my original change and
implementing David's and Karen's proposals.

David, I think the change addresses a row of your concerns.


proposal - it just moves the "field" from the Java side to the VM side.

No, the space overhead in case of successful initialization is
reduced from O(classes) to O(classloaders).


I don't think space usage was one of my concerns.


There is only runtime overhead if there was an error, and
that should be acceptable.


dealing with backtrace and stackTrace. I have to wonder why nothing in
Throwable clears the backtrace today ?

Maybe the concern about the backTraces is pointless and the
conversion to stackTraces should be dropped.
As you say, it's done nowhere else, and other backTraces should
cause similar issues.


My comment on the clearing of Throwable.backtrace was purely in relation 
to the internal state protocol of Throwable, not about the conversion 
process as such.


The conversion process is needed, as previously discussed, to avoid 
class/classloader leaks. Exceptions are normally thread-contained so 
there is limited scope for creating class/classloader leaks. However as 
soon as you allow this original exception to be stored and potentially 
later accessed in a different thread then the leak is possible - it 
might even be possible to hit some kind of loader constraint error if a 
same named type can be loaded by different classloaders used by the 
different threads.


Add some extra information about the type of the original exception by 
all means, but not the stacktrace (unless you've stringified it). As I 
say the place the stack is of interest in when the original exception is 
encountered. If something is swallowing this then logging is the tool to 
expose it IMHO.



I'm not clear why you record the ExceptionInInitializerError wrapper
instead of the actual exception that occurred?

Keeping the ExceptionInInitializerError is helpful for people
to understand that this has happened during initialization and not directly
where the NCDFE is thrown. They will understand that this might have
happened in another thread.


The NCDFE  message already states something like "cannot initialize 
class because prior initialization attempt failed", which I think makes 
it quite clear.


I dispute "they will understand this might have happened in another thread".

Cheers,
David


Best regards,
   Goetz.







-Original Message-
From: core-libs-dev [mailto:core-libs-dev-boun...@openjdk.java.net] On
Behalf Of David Holmes
Sent: Sonntag, 1. Juli 2018 23:48
To: Volker Simonis ; hotspot-runtime-
d...@openjdk.java.net runtime ;
Java Core Libs 
Subject: Re: RFR(M): 8203826: Chain class initialization exceptions into later
NoClassDefFoundErrors

Hi Volker,

This doesn't really address any of the concerns I had with the original
proposal - it just moves the "field" from the Java side to the VM side.
There is still a massive amount of Java code execution in relation to
this - which itself may encounter secondary exceptions. It's very hard
to tell if you will leave things in a suitable state if such exceptions
arise.

My position remains that the primary place to deal with the
initialization error is when initialization occurs and the error
happens. Subsequent attempted uses of the erroneous class may benefit
from some additional information about the nature of the original
exceptions, but I don't think full stacktraces are necessary or
desirable (and I do believe they will confuse most users given the lack
of continuity in the stack frames and that they may have happened in a
different thread!).

That aside ...

There appears to a race on constructing the Hashtable. At least it was
not obvious to me where a lock may be held during that process.

I can't determine that clearing backtrace in removeNativeBacktrace is
correct with respect to the overall protocol within Throwable for
dealing with backtrace and stackTrace. I have to wonder why nothing in
Throwable clears the backtrace today ?

I'm not clear why you record the ExceptionInInitializerError wrapper
instead of the actual exception that occurred?

Throwable states:

+  * This method is currently only called from the VM for instances of
+  * ExceptionInInitializerError which are stored for later chaining
into a
+  * NoClassDefFoundError in order to prevent keeping classes from
the native
+  * backtrace alive.
+  */

but IIUC it will also be called for instances of Error that occurred
which do not get wrapped in EIIE.


Regards,
David
--


On 30/06/2018 12:53 AM, Volker Simonis wrote:

Hi,

can I please have a review for the following change which saves
ExceptionInInitializerError thrown during class initialization and
chains them as cause into potential NoClassDefFoundErrors for the same
class. We are using this 

Re: RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors

2018-07-04 Thread Peter Levart

Hi Volker,

It occurred to me that getting rid of backtrace-s of cause(s)/suppressed 
exception(s) might not be enough to prevent ClassLoader leaks...


On 07/04/2018 10:21 AM, Lindenmaier, Goetz wrote:

dealing with backtrace and stackTrace. I have to wonder why nothing in
Throwable clears the backtrace today ?

Maybe the concern about the backTraces is pointless and the
conversion to stackTraces should be dropped.
As you say, it's done nowhere else, and other backTraces should
cause similar issues.



Exception objects are typically not retained for longer periods. They 
are normally caught, dumped to log and let gone. This change retains 
exception(s) so that they are reachable from a ClassLoader that loaded 
the failed class. It could happen that the chain of cause(s)/suppressed 
exception(s) of some ExceptionInInitializerError is an exception object 
of a class that is loaded by some child ClassLoader of the ClassLoader 
that loaded the failed class. Such child ClassLoader would have leaked.


The solution would be to replace the chain of cause(s)/suppressed 
exception(s) with a chain of replacement exception objects like this one 
(this would also take care of backtraces of original exceptions as it 
would not retain the original exceptions at all):



/**
 * A {@link RuntimeException} that acts as a substitute for the 
original exception
 * (checked or unchecked) and mimics the original exception in every 
aspect except it's type.

 */
public class ExceptionSubstitute extends RuntimeException {
    private static final long serialVersionUID = 1;

    private String originalExceptionClassName, localizedMessage;

    public ExceptionSubstitute(Throwable originalException) {
    super(originalException.getMessage());

    this.originalExceptionClassName = 
originalException.getClass().getName();

    this.localizedMessage = originalException.getLocalizedMessage();

    // substitute originalException's cause
    Throwable cause = originalException.getCause();
    initCause(cause == null ? null : new ExceptionSubstitute(cause));

    // substitute originalException's suppressed exceptions if any
    for (Throwable suppressed : originalException.getSuppressed()) {
    addSuppressed(new ExceptionSubstitute(suppressed));
    }

    // inherit stack trace elements from originalException
    setStackTrace(originalException.getStackTrace());
    }

    @Override
    public Throwable fillInStackTrace() {
    // don't need our backtrace - will inherit stack trace elements 
from originalException

    return this;
    }

    @Override
    public String getLocalizedMessage() {
    return localizedMessage;
    }

    /**
 * @return the class name of the original exception for which this 
exception is a substitute

 */
    public String getOriginalExceptionClassName() {
    return originalExceptionClassName;
    }

    /**
 * Emulate toString() method as if called upon originalException
 */
    @Override
    public String toString() {
    String message = getLocalizedMessage();
    return (message != null)
   ? (getOriginalExceptionClassName() + ": " + message)
   : getOriginalExceptionClassName();
    }
}


Regards, Peter



RE: RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors

2018-07-04 Thread Lindenmaier, Goetz
Hi,

Volker, thanks for improving on my original change and 
implementing David's and Karen's proposals.

David, I think the change addresses a row of your concerns.

> proposal - it just moves the "field" from the Java side to the VM side.
No, the space overhead in case of successful initialization is 
reduced from O(classes) to O(classloaders).

There is only runtime overhead if there was an error, and 
that should be acceptable. 

> dealing with backtrace and stackTrace. I have to wonder why nothing in
> Throwable clears the backtrace today ?
Maybe the concern about the backTraces is pointless and the 
conversion to stackTraces should be dropped.
As you say, it's done nowhere else, and other backTraces should
cause similar issues. 

> I'm not clear why you record the ExceptionInInitializerError wrapper
> instead of the actual exception that occurred?
Keeping the ExceptionInInitializerError is helpful for people
to understand that this has happened during initialization and not directly 
where the NCDFE is thrown. They will understand that this might have 
happened in another thread. 

Best regards,
  Goetz.






> -Original Message-
> From: core-libs-dev [mailto:core-libs-dev-boun...@openjdk.java.net] On
> Behalf Of David Holmes
> Sent: Sonntag, 1. Juli 2018 23:48
> To: Volker Simonis ; hotspot-runtime-
> d...@openjdk.java.net runtime ;
> Java Core Libs 
> Subject: Re: RFR(M): 8203826: Chain class initialization exceptions into later
> NoClassDefFoundErrors
> 
> Hi Volker,
> 
> This doesn't really address any of the concerns I had with the original
> proposal - it just moves the "field" from the Java side to the VM side.
> There is still a massive amount of Java code execution in relation to
> this - which itself may encounter secondary exceptions. It's very hard
> to tell if you will leave things in a suitable state if such exceptions
> arise.
> 
> My position remains that the primary place to deal with the
> initialization error is when initialization occurs and the error
> happens. Subsequent attempted uses of the erroneous class may benefit
> from some additional information about the nature of the original
> exceptions, but I don't think full stacktraces are necessary or
> desirable (and I do believe they will confuse most users given the lack
> of continuity in the stack frames and that they may have happened in a
> different thread!).
> 
> That aside ...
> 
> There appears to a race on constructing the Hashtable. At least it was
> not obvious to me where a lock may be held during that process.
> 
> I can't determine that clearing backtrace in removeNativeBacktrace is
> correct with respect to the overall protocol within Throwable for
> dealing with backtrace and stackTrace. I have to wonder why nothing in
> Throwable clears the backtrace today ?
> 
> I'm not clear why you record the ExceptionInInitializerError wrapper
> instead of the actual exception that occurred?
> 
> Throwable states:
> 
> +  * This method is currently only called from the VM for instances of
> +  * ExceptionInInitializerError which are stored for later chaining
> into a
> +  * NoClassDefFoundError in order to prevent keeping classes from
> the native
> +  * backtrace alive.
> +  */
> 
> but IIUC it will also be called for instances of Error that occurred
> which do not get wrapped in EIIE.
> 
> 
> Regards,
> David
> --
> 
> 
> On 30/06/2018 12:53 AM, Volker Simonis wrote:
> > Hi,
> >
> > can I please have a review for the following change which saves
> > ExceptionInInitializerError thrown during class initialization and
> > chains them as cause into potential NoClassDefFoundErrors for the same
> > class. We are using this features since years in our commercial SAP
> > JVM and it proved extremely useful for detecting and fixing errors
> > especially in big deployments.
> >
> > This is a follow-up on a discussion previously started by Goetz [1].
> > His first proposal (which is close to our current, internal
> > implementation) inserted an additional field into java.lang.Class
> > objects to save potential ExceptionInInitializerErrors. This was
> > considered to much overhead in the initial discussion [1].
> >
> > http://cr.openjdk.java.net/~simonis/webrevs/2018/8203826.v2/
> > https://bugs.openjdk.java.net/browse/JDK-8203826
> >
> > So in this change, I've completely re-implemented the feature by using
> > a java.lang.Hashtable which is attached to the ClassLoaderData object.
> > The Hashtable is lazily created when the first
> > ExceptionInInitializerError is thrown and maps the Class which
> >

Re: RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors

2018-07-01 Thread David Holmes

Hi Volker,

This doesn't really address any of the concerns I had with the original 
proposal - it just moves the "field" from the Java side to the VM side. 
There is still a massive amount of Java code execution in relation to 
this - which itself may encounter secondary exceptions. It's very hard 
to tell if you will leave things in a suitable state if such exceptions 
arise.


My position remains that the primary place to deal with the 
initialization error is when initialization occurs and the error 
happens. Subsequent attempted uses of the erroneous class may benefit 
from some additional information about the nature of the original 
exceptions, but I don't think full stacktraces are necessary or 
desirable (and I do believe they will confuse most users given the lack 
of continuity in the stack frames and that they may have happened in a 
different thread!).


That aside ...

There appears to a race on constructing the Hashtable. At least it was 
not obvious to me where a lock may be held during that process.


I can't determine that clearing backtrace in removeNativeBacktrace is 
correct with respect to the overall protocol within Throwable for 
dealing with backtrace and stackTrace. I have to wonder why nothing in 
Throwable clears the backtrace today ?


I'm not clear why you record the ExceptionInInitializerError wrapper 
instead of the actual exception that occurred?


Throwable states:

+  * This method is currently only called from the VM for instances of
+  * ExceptionInInitializerError which are stored for later chaining 
into a
+  * NoClassDefFoundError in order to prevent keeping classes from 
the native

+  * backtrace alive.
+  */

but IIUC it will also be called for instances of Error that occurred 
which do not get wrapped in EIIE.



Regards,
David
--


On 30/06/2018 12:53 AM, Volker Simonis wrote:

Hi,

can I please have a review for the following change which saves
ExceptionInInitializerError thrown during class initialization and
chains them as cause into potential NoClassDefFoundErrors for the same
class. We are using this features since years in our commercial SAP
JVM and it proved extremely useful for detecting and fixing errors
especially in big deployments.

This is a follow-up on a discussion previously started by Goetz [1].
His first proposal (which is close to our current, internal
implementation) inserted an additional field into java.lang.Class
objects to save potential ExceptionInInitializerErrors. This was
considered to much overhead in the initial discussion [1].

http://cr.openjdk.java.net/~simonis/webrevs/2018/8203826.v2/
https://bugs.openjdk.java.net/browse/JDK-8203826

So in this change, I've completely re-implemented the feature by using
a java.lang.Hashtable which is attached to the ClassLoaderData object.
The Hashtable is lazily created when the first
ExceptionInInitializerError is thrown and maps the Class which
triggered the ExceptionInInitializerError during the execution of its
static initializer to the corresponding ExceptionInInitializerError.

If the same class will be accessed once again, this will directly lead
to a plain NoClassDefFoundError (as per the JVMS, 5.5 Initialization)
because the static initializer won't be executed a second time. Until
now, this NoClassDefFoundError wasn't linked in any way to the root
cause of the problem (i.e. the first ExceptionInInitializerError
together with the chained exception that happened during the execution
of the static initializer). With this change, the NoClassDefFoundError
will now chain the initial ExceptionInInitializerError as cause,
making it much easier to detect the problem which lead to the
NoClassDefFoundError.

Following is an example from the new JTreg tests which comes which
this change to demonstrate the feature. Until know, a typical stack
trace from a NoClassDefFoundError looked as follows:

java.lang.NoClassDefFoundError: Could not initialize class
NoClassDefFound$ClassWithFailedInitializer
 at java.base/java.lang.Class.forName0(Native Method)
 at java.base/java.lang.Class.forName(Class.java:291)
 at NoClassDefFound.main(NoClassDefFound.java:38)

With this change, the same stack trace now looks as follows:

java.lang.NoClassDefFoundError: Could not initialize class
NoClassDefFound$ClassWithFailedInitializer
 at java.base/java.lang.Class.forName0(Native Method)
 at java.base/java.lang.Class.forName(Class.java:315)
 at NoClassDefFound.main(NoClassDefFound.java:38)
Caused by: java.lang.ExceptionInInitializerError
 at 
java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native
Method)
 at 
java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
 at 
java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 at 
java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
 at jav

Re: RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors

2018-06-30 Thread Peter Levart

Hi Volker,

I fully support this change, although I'm not qualified to review it as 
I'm not so acquainted with VM internals. The code looks reasonable to me 
though. This change will greatly help track down class initializiation 
bugs. I don't think that attaching a cause which is not directly an 
exception caught at the place where new exception is thrown will confuse 
people much, although I can imagine that they are used to observe such 
chains most of the time. The stack traces of cause and top exception 
clearly show where their source is and as you say the type of cause 
(ExceptionInInitializerError) hints at that too. Another hint that you 
may consider adding or not, depending on whether it would be possible to 
implement elegantly, is for the cause to somehow include the name of the 
thread in which it occurred in its message. When the top exception 
(NoClassDefFoundError) is caught and logged, the logger usually includes 
the thread name. If the cause also included thread name and it was 
different, it would be another hint to the user of what actually happened.


Just some comments / questions about the test. This is really quite a 
complicated play of exceptions, causes and initialization orders, I must 
say.


I'll try to evaluate my reasoning by describing what test does, so let's 
start with this:


 109 public static class A {
 110 public static final int i = (new Object[1])[2].hashCode();
 111 }
 112 public static class B extends A {}
 113 public static class C extends B {}

Those 3 classes are designed so that their initialization fails. 
Initialization of A fails because ArrayIndexOutOfBoundsException is 
thrown in field initializer, initialization of B fails because it is a 
subclass of A and initialization of C fails because it is a subclass of B.


On top of that, the test includes the following class:

 115 static class ClassWithFailedInitializer {
 116 public static final int i;
 117 static {
 118 cl = new URLClassLoader(new URL[] {
 119 
ClassWithFailedInitializer.class.getProtectionDomain().getCodeSource().getLocation() 
}, null);

 120 try {
 121 cl.loadClass("NoClassDefFoundErrorTest$C").newInstance();
 122 } catch (ClassNotFoundException | 
InstantiationException | IllegalAccessException e) {
 123 throw new RuntimeException("Class should be 
found", e);

 124 }
 125 i = 42;
 126 }
 127 }

...and pulls the trigger with:

 137 if (ClassWithFailedInitializer.i == 0) {

This triggers initialization of ClassWithFailedInitializer 1st, which 
constructs new ClassLoader which it then uses to load class C and 
attempts to instantiate an object from it. You then have the following 
handler arround the last two actions:


 120 try {
 121 cl.loadClass("NoClassDefFoundErrorTest$C").newInstance();
 122 } catch (ClassNotFoundException | 
InstantiationException | IllegalAccessException e) {
 123 throw new RuntimeException("Class should be 
found", e);

 124 }

ClassNotFoundException should not be thrown as C class should be 
loadable. That's OK. The other two exceptions that you catch here are a 
little confusing, because they can only occur after successful class C 
initialization and because they are impossible exceptions in the 
concrete setup. But I can understand that you have to handle them 
somehow because they are checked exceptions. So perhaps you could group 
them into another catch block and throw RuntimeException with a more 
descriptive message (like "Impossible situation" or such). Just to be 
less confusing for someone who would have to read the code in the future...


Then we get to the following confusing point:

 136 try {
 137 if (ClassWithFailedInitializer.i == 0) {
 138 throw new RuntimeException("Class initialization 
succeeded but is designed to fail.");

 139 }
 140 throw new Exception("Expected exception was not thrown.");
 141 }
 142 catch (ExceptionInInitializerError e) {
 143 e.printStackTrace();
 144 Asserts.assertNE(e.getCause(), null, "Expecting cause 
in ExceptionInInitializerError.");
 145 Asserts.assertEQ(e.getCause().getClass(), 
ArrayIndexOutOfBoundsException.class, "sanity");

 146 }

I think that line 138 is not reachable. If ClassWithFailedInitializer.i 
== 0 evaluated successfully, it would mean that 
ClassWithFailedInitializer initialization finished normally, but if it 
finished normally, then field i would contain 42, wouldn't it? I think 
you wanted the test to be if (ClassWithFailedInitializer.i == 42) and 
then you don't need line 140. Am I right? The same goes for line 156.


Good work.

Regards, Peter


On 06/29/18 16:53, Volker Simonis wrote:

Hi,

can I please have a review for the following change which saves
ExceptionInInitializer