United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: 6805864 Problem with jvmti->redefineClasses: some methods don't get redefined
6805864 : Problem with jvmti->redefineClasses: some methods don't get redefined

Details
Type:
Bug
Submit Date:
2009-02-16
Status:
Closed
Updated Date:
2013-02-01
Project Name:
JDK
Resolved Date:
2011-03-08
Component:
hotspot
OS:
linux,generic,solaris_10
Sub-Component:
jvmti
CPU:
x86,generic
Priority:
P3
Resolution:
Fixed
Affected Versions:
hs10,hs11,hs14
Fixed Versions:
hs15

Related Reports
Backport:
Backport:
Backport:
Backport:
Relates:
Relates:

Sub Tasks

Description
Description of the problem:

We found a method where redefineClasses doesn't work as expected.

If we instrument a method during the ClassFileLoadHook, the transformed method is used. After we uninstrumented the method using redefineClasses and call the method, the old(instrumented) method is used.

We first thought that there are problems unjitting the method, but the problem occurred with -Xint too. All 1.6.0 VMs are affected, even jdk 1.6.0_11 (1.5 VMs haven't been tested).

A simple reproducer with a launch script for linux is attached.

The reproducer exchange the
org.hibernate.persister.entity.AbstractEntityPersister class during the ClassFileLoadHook with a version where the isVersioned method print its name. 
Methods from this class get called every two seconds. After 5 seconds the class is redefined with a version where another method print its name, and the isVersioned method is uninstrumented. If these methods are called, hasCascades prints hasCascades - as expected - but isVersioned also prints isVersioned.
I added support to the TraceRedefineClasses option to catch
the case where an obsolete method is being called. Here are
snippets of the info gathered:

RedefineClasses-0x1000: calling obsolete method 'org.hibernate.persister.entity.AbstractEntityPersister.isVersioned()Z'

#  Internal Error (src/share/vm/runtime/sharedRuntime.cpp:413)
#  Error: guarantee(false,"should never call an obsolete method.")

Here is the stack trace from a decoded hs_err_pid file:

V  [libjvm.so+0x92812f];;  __1cNSharedRuntimeVrc_trace_method_entry6FpnKJavaThread_pnNmethodOopDesc__i_+0x263
j  org.hibernate.persister.entity.AbstractEntityPersister.isVersioned()Z+0
j  org.hibernate.event.def.AbstractSaveEventListener.substituteValuesIfNecessary(Ljava/lang/Object;Ljava/io/Serializable;[Ljava/lang/Object;Lorg/hibernate/persister/entity/EntityPersister;Lorg/hibernate/engine/SessionImplementor;)Z+33
j  org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(Ljava/lang/Object;Lorg/hibernate/engine/EntityKey;Lorg/hibernate/persister/entity/EntityPersister;ZLjava/lang/Object;Lorg/hibernate/event/EventSource;Z)Ljava/io/Serializable;+155
j  org.hibernate.event.def.AbstractSaveEventListener.performSave(Ljava/lang/Object;Ljava/io/Serializable;Lorg/hibernate/persister/entity/EntityPersister;ZLjava/lang/Object;Lorg/hibernate/event/EventSource;Z)Ljava/io/Serializable;+202
j  org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;Lorg/hibernate/event/EventSource;Z)Ljava/io/Serializable;+193
j  org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(Lorg/hibernate/event/PersistEvent;Ljava/util/Map;)V+55
j  org.hibernate.event.def.DefaultPersistEventListener.onPersist(Lorg/hibernate/event/PersistEvent;Ljava/util/Map;)V+180
j  org.hibernate.event.def.DefaultPersistEventListener.onPersist(Lorg/hibernate/event/PersistEvent;)V+7
j  org.hibernate.impl.SessionImpl.firePersist(Lorg/hibernate/event/PersistEvent;)V+28
j  org.hibernate.impl.SessionImpl.persist(Ljava/lang/String;Ljava/lang/Object;)V+11
j  org.hibernate.impl.SessionImpl.persist(Ljava/lang/Object;)V+3
j  Misc.urks(Lorg/hibernate/Session;)V+15
j  Misc.main([Ljava/lang/String;)V+77
v  ~StubRoutines::call_stub
V  [libjvm.so+0x4821c7];;  __1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x6c3


I'll take a look at org.hibernate.event.def.AbstractSaveEventListener.
substituteValuesIfNecessary() to see how it is calling
org.hibernate.persister.entity.AbstractEntityPersister.isVersioned().
That might tell me if we missed something during redefinition.
The call in org.hibernate.event.def.AbstractSaveEventListener.
substituteValuesIfNecessary() looks like this:

   31:  aload   4
   33:  invokeinterface #488,  1; //InterfaceMethod org/hibernate/persister/entity/EntityPersister.isVersioned:()Z
   38:  ifeq    76

I remember that TraceRedefineClasses output told me that there
were itable updates for isVersioned(), but I need to take a
closer look.
I've attached the reproducer.tgz file provided by the customer.
I've attached my initial draft of RedefineAbstractClass.sh to
this bug. This version of the test was written relative to
JDK6 so I'll have to tweak it for JDK7.

                                    

Comments
EVALUATION

http://hg.openjdk.java.net/jdk7/hotspot-rt/hotspot/rev/70998f2e05ef
                                     
2009-03-03
SUGGESTED FIX

See the attached 6805864-webrev-cr0.tgz for the proposed fix.
                                     
2009-02-23
EVALUATION

In the provided test case, the target class
(org.hibernate.persister.entity.AbstractEntityPersister) is
an abstract class that implements more than one interface.
The isVersioned() method is also specified by more than
interface. So the isVersioned() method that is defined by
the org.hibernate.persister.entity.AbstractEntityPersister
abstract class applies to more than one interface so it
appears in more than one itable entry for the abstract class
org.hibernate.persister.entity.AbstractEntityPersister.

The RedefineClasses() implementation does not expect more
than one itable entry to contain a matching method when it
is updating the itable for a target class.

I'm generating a small test case to illustrate the bug.
This test will be added to the JDI_REGRESSION test suite.
                                     
2009-02-18



Hardware and Software, Engineered to Work Together