JDK-7064927 : retransformClasses() does not pass in LocalVariableTable of a method
  • Type: Bug
  • Component: hotspot
  • Sub-Component: jvmti
  • Affected Version: 6u26
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86
  • Submitted: 2011-07-10
  • Updated: 2012-08-14
  • Resolved: 2012-01-20
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8 Other
7u4Fixed 8Fixed hs23Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02, mixed mode)

Microsoft Windows [Version 6.1.7601]


If java.lang.instrument.Instrumentation.retransformsClasses() is called on a class that had already been loaded, then the jvm doesnt seem to pass in the LocalVariableTable for the methods of the class.
This prevents any instrumentation which needs local variable info.

If the same class is instrumented normally using a transformer as it is loaded the LocalVariableTable is indeed passed.

Is this a bug in the JVM and if so where should this be posted so it gets noticed?

This bug has been verified in both java 6update 20 and update 26.

1. Write a program that uses a class, say Foo
2. Have some mechanism, maybe a button click which calls java.lang.instrument.Instrumentation.retransformClasses() on Foo, which has already been loaded by the JVM.
3. The byte array passed to the instrumentor in retransformClasses() does not contain the LocalVariableTable of the methods of class Foo.

The LocalVariableTable should be present when instrumenting the class through retransformClasses() method.
The LocalVariableTable is not passed

This bug can be reproduced always.

EVALUATION http://hg.openjdk.java.net/lambda/lambda/hotspot/rev/c01e115b095e

EVALUATION http://hg.openjdk.java.net/hsx/hotspot-rt/hotspot/rev/c01e115b095e

EVALUATION The JVM/TI RetransformClasses() API uses the JvmtiClassFileReconstituter class to get the bytecode to pass to the ClassFileLoadHook event. The src/share/vm/prims/jvmtiClassFileReconstituter.cpp file has the following interesting comments: // FIXME: add Deprecated, LVT, LVTT attributes // FIXME: fix Synthetic attribute // FIXME: per Serguei, add error return handling for constantPoolOopDesc::copy_cpool_bytes() <snip> void JvmtiClassFileReconstituter::write_code_attribute(methodHandle method) { constMethodHandle const_method(thread(), method->constMethod()); u2 line_num_cnt = 0; int stackmap_len = 0; // compute number and length of attributes -- FIXME: for now no LVT <snip> if (stackmap_len != 0) { write_stackmap_table_attribute(method, stackmap_len); } // FIXME: write LVT attribute } I'm moving this bug from java/java/classes_instrument to jvm/hotspot/jvmti.

EVALUATION In /src/share/vm/classfile/classFileParser.cpp, in the function ClassFileParser::parse_method(), the following block of code is interesting: /* Copy class file LVT's/LVTT's into the HotSpot internal LVT. * * Rules for LVT's and LVTT's are: * - There can be any number of LVT's and LVTT's. * - If there are n LVT's, it is the same as if there was just * one LVT containing all the entries from the n LVT's. * - There may be no more than one LVT entry per local variable. * Two LVT entries are 'equal' if these fields are the same: * start_pc, length, name, slot * - There may be no more than one LVTT entry per each LVT entry. * Each LVTT entry has to match some LVT entry. * - HotSpot internal LVT keeps natural ordering of class file LVT entries. */ if (total_lvt_length > 0) { int tbl_no, idx; promoted_flags->set_has_localvariable_table(); LVT_Hash** lvt_Hash = NEW_RESOURCE_ARRAY(LVT_Hash*, HASH_ROW_SIZE); initialize_hashtable(lvt_Hash); // To fill LocalVariableTable in Classfile_LVT_Element* cf_lvt; LocalVariableTableElement* lvt = m->localvariable_table_start(); for (tbl_no = 0; tbl_no < lvt_cnt; tbl_no++) { cf_lvt = (Classfile_LVT_Element *) localvariable_table_start[tbl_no]; for (idx = 0; idx < localvariable_table_length[tbl_no]; idx++, lvt++) { copy_lvt_element(&cf_lvt[idx], lvt); // If no duplicates, add LVT elem in hashtable lvt_Hash. if (LVT_put_after_lookup(lvt, lvt_Hash) == false && _need_verify && _major_version >= JAVA_1_5_VERSION ) { clear_hashtable(lvt_Hash); classfile_parse_error("Duplicated LocalVariableTable attribute " "entry for '%s' in class file %s", cp->symbol_at(lvt->name_cp_index)->as_utf8(), CHECK_(nullHandle)); } } } // To merge LocalVariableTable and LocalVariableTypeTable Classfile_LVT_Element* cf_lvtt; LocalVariableTableElement lvtt_elem; for (tbl_no = 0; tbl_no < lvtt_cnt; tbl_no++) { cf_lvtt = (Classfile_LVT_Element *) localvariable_type_table_start[tbl_no]; for (idx = 0; idx < localvariable_type_table_length[tbl_no]; idx++) { copy_lvt_element(&cf_lvtt[idx], &lvtt_elem); int index = hash(&lvtt_elem); LVT_Hash* entry = LVT_lookup(&lvtt_elem, index, lvt_Hash); if (entry == NULL) { if (_need_verify) { clear_hashtable(lvt_Hash); classfile_parse_error("LVTT entry for '%s' in class file %s " "does not match any LVT entry", cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), CHECK_(nullHandle)); } } else if (entry->_elem->signature_cp_index != 0 && _need_verify) { clear_hashtable(lvt_Hash); classfile_parse_error("Duplicated LocalVariableTypeTable attribute " "entry for '%s' in class file %s", cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), CHECK_(nullHandle)); } else { // to add generic signatures into LocalVariableTable entry->_elem->signature_cp_index = lvtt_elem.descriptor_cp_index; } } } clear_hashtable(lvt_Hash); } It looks like the LVT (and LVTT) are copied out when the class file is parsed and put on the side.