On 10/22/18 10:56 AM, Ioi Lam wrote:



On 10/22/18 10:25 AM, Jiangli Zhou wrote:
Hi Ioi,

Looks good. Please see comments below.

- src/hotspot/share/classfile/javaClasses.cpp

4254     assert(JvmtiEnvBase::get_phase() <= JVMTI_PHASE_PRIMORDIAL,
4255            "Field offsets of well-known classes must be computed in JVMTI_PHASE_PRIMORDIAL or before");

Maybe it is worth adding a function (for example, is_primordial()) in jvmtiEnvBase.hpp, so we can avoid using JVMTI details here?

I'll add JvmtiExport::is_early_phase(), since the phase can be either JVMTI_PHASE_ONLOAD or  JVMTI_PHASE_PRIMORDIAL.

I'm not too sure if the assert is necessary. Why well known-classes' field offsets must be computed in JVMTI_PHASE_PRIMORDIAL or before? Currently they are, however that's because they are used early by the VM. That doesn't directly relate to any JVMTI phase necessarily. With the assert, we are explicitly making a connection to the JVMTI phases. To me, that doesn't seem to be necessary.


JavaClasses::compute_offsets uses many different classes. I would need to check that each of them were in the well-known class list, so that we know the offsets stored in the CDS archive are still valid. However, I couldn't find a single place to make such a check, and it would be much easier to add the above assert, which means any shared class used inside compute_offsets cannot be replaced by JVMTI.

How about this:

void JavaClasses::compute_offsets() {
  if (UseSharedSpaces) {
    assert(JvmtiEnvBase::is_early_phase() && !JvmtiExport::has_early_class_hook_env(),            "JavaClasses::compute_offsets() must be called in early JVMTI phase.");     // None of the classes used by the rest of this function can be replaced by
    // JMVTI ClassFileLoadHook.
    // We are safe to use the archived offsets, which have already been restored     // by JavaClasses::serialize_offsets, without computing the offsets again.
    return;
  }

You could do assert(k->is_shared() || !UseSharedSpaces) in the DO_SERIALIZE_OFFSETS macro to make sure the expected shared classes are used when UseSharedSpaces is enabled during loading the archived field offsets. The extra !UseSharedSpaces here is because serialize_offsets() function is used for both dump time and runtime. It could be removed because the 'is_shared' flag is probably already set when we writing out data at dump time, but please double check that.

#define DO_SERIALIZE_OFFSETS(k) k::serialize_offsets(soc);

If JvmtiExport::early_class_hook_env is requested by a JVMTI agent, UseSharedSpaces should already be disabled at runtime, otherwise a shared class should be used in this case.



- src/hotspot/share/classfile/systemDictionary.cpp

2108   if (UseSharedSpaces) {
2109     assert(JvmtiEnvBase::get_phase() <= JVMTI_PHASE_PRIMORDIAL,
2110            "All well known classes must be resolved in JVMTI_PHASE_PRIMORDIAL or before");
2111     for (int i = FIRST_WKID; i < last; i++) {
2112       InstanceKlass* k = _well_known_klasses[i];
2113       assert(k->is_shared(), "must not be replaced by JVMTI class file load hook");
2114     }

Please include the above block under #ifdef ASSERT.

OK

-// preloading is actually performed by resolve_preloaded_classes.
+// class resolution is actually performed by resolve_well_known_classes.

The original comment is more accurate. Maybe use 'eager loading' if you want to avoid the confusion between 'preloading' and CDS's term?

I can see that "class resolution" could have different meanings, although resolve_well_known_classes does call SystemDictionary::resolve_or_fail, not any the SystemDictionary::load* functions. So using the word "resolve" would be more appropriate.

How about changing the comments to the following to avoid ambiguity.

#define WK_KLASS_ENUM_NAME(kname)    kname##_knum

// Certain classes, such as java.lang.Object and java.lang.String,
// are "well-known", in the sense that no class loader is allowed
// to provide a different definition.
//
// Each well-known class has a short klass name (like object_klass),
// and a vmSymbol name (like java_lang_Object).
//
// The order of these definitions is significant: the classes are
// resolved during early VM start-up by resolve_well_known_classes
// in this order. Changing the order may require careful restructuring
// of the VM start-up sequence.
//
#define WK_KLASSES_DO(do_klass) ......

Looks ok.

Thanks,
Jiangli


Thanks
- Ioi
The test looks good. Thanks for filling the gap in this area!

Thanks,
Jiangli

On 10/21/18 10:49 PM, Ioi Lam wrote:
Hi David,

Thanks for the review. Updated webrev:

http://cr.openjdk.java.net/~iklam/jdk12/8212200-cds-jvmti-clfh-critical-classes.v04/ http://cr.openjdk.java.net/~iklam/jdk12/8212200-cds-jvmti-clfh-critical-classes.v04.delta/

More comments below:



On 10/21/18 6:57 PM, David Holmes wrote:
Hi Ioi,

Generally seems okay.

On 22/10/2018 11:15 AM, Ioi Lam wrote:
Re-sending to the correct mailing lists. Please disregard the other email.

http://cr.openjdk.java.net/~iklam/jdk12/8212200-cds-jvmti-clfh-critical-classes.v03/
https://bugs.openjdk.java.net/browse/JDK-8212200

Hi,

CDS has various built-in assumptions that classes loaded by
SystemDictionary::resolve_well_known_classes must not be replaced
by JVMTI ClassFileLoadHook during run time, including

- field offsets computed in JavaClasses::compute_offsets
- the layout of the strings objects in the shared strings table

The "well-known" classes can be replaced by ClassFileLoadHook only
when JvmtiExport::early_class_hook_env() is true. Therefore, the
fix is to disable CDS under this condition.

I'm a little unclear why we have to iterate JvmtiEnv list when this has to be checked during JVMTI_PHASE_PRIMORDIAL?

I think you are asking about this new function? I don't like the name "early_class_hook_env()". Maybe I should change it to "has_early_class_hook_env()"?


bool JvmtiExport::early_class_hook_env() {
  JvmtiEnvIterator it;
  for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
    if (env->early_class_hook_env()) {
      return true;
    }
  }
  return false;
}

This function matches condition in the existing code that would call into ClassFileLoadHook:

class JvmtiClassFileLoadHookPoster {
 ...
  void post_all_envs() {
    JvmtiEnvIterator it;
    for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
        ..
        post_to_env(env, true);
    }
  }
...
  void post_to_env(JvmtiEnv* env, bool caching_needed) {
    if (env->phase() == JVMTI_PHASE_PRIMORDIAL && !env->early_class_hook_env()) {
      return;
    }


post_all_envs() is called just before a class is about to be loaded in the JVM. So if *any* env->early_class_hook_env() returns true, there's a chance that it will replace a well-known class.So, as a preventive measure, CDS will be disabled.



I have added a few test cases to try to replace shared classes,
including well-known classes and other classes. See
comments in ReplaceCriticalClasses.java for details.

As a clean up, I also renamed all use of "preloaded" in
the source code to "well-known". They refer to the same thing
in HotSpot, so there's no need to use 2 terms. Also, The word
"preloaded" is ambiguous -- it's unclear when "preloading" happens,
and could be confused with what CDS does during archive dump time.

A few specific comments:

src/hotspot/share/classfile/systemDictionary.cpp

+ bool SystemDictionary::is_well_known_klass(Symbol* class_name) {
+   for (int i = 0; ; i++) {
+     int sid = wk_init_info[i];
+     if (sid == 0) {
+       break;
+     }

I take it a zero value is a guaranteed end-of-list sentinel?


Yes. The array is defined just a few lines above:

static const short wk_init_info[] = {
  #define WK_KLASS_INIT_INFO(name, symbol) \
    ((short)vmSymbols::VM_SYMBOL_ENUM_NAME(symbol)),

  WK_KLASSES_DO(WK_KLASS_INIT_INFO)
  #undef WK_KLASS_INIT_INFO
  0
};

Also,

class vmSymbols: AllStatic {
  enum SID {
    NO_SID = 0,
    ....



+ for (int i=FIRST_WKID; i<last; i++) {

Style nit: need spaces around = and <


Fixed.

---

test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/ReplaceCriticalClasses.java

New file should have current copyright year only.

Fixed.

 31  * @comment CDS should not be disabled -- these critical classes will be replaced because JvmtiExport::early_class_hook_env() is true.

Comment seems contradictory: if we replace critical classes then CDS should be disabled right??

Fixed.

I expected to see tests that checked for:

"CDS is disabled because early JVMTI ClassFileLoadHook is in use."

in the output. ??

<rant>
It would have been easy if jtreg lets you check the output of @run easily. Instead, your innocent suggestion has turned into 150+ lines of new code :-( Maybe "let's write all shell tests in Java" isn't such a great idea after all.
</rant>

Now the test checks that whether CDS is indeed disabled, whether the affected class is loaded from the shared archive, etc.

Thanks
- Ioi

Thanks,
David



 > In early e-mails Jiangli wrote:
 >
 > We should consider including more classes from the default classlist  > in the test. Archived classes loaded during both 'early' phase and after
 > should be tested.

Done.


 > For future optimizations, we might want to prevent loading additional
 > shared classes if any of the archived system classes is changed.

What's the benefit of doing this? Today we already stop loading a shared
class if its super class was not loaded from the archive.


Thanks
- Ioi





Reply via email to