gt:             AgentVMOption.Info info = AgentVMOption.getInfo(i);
context: /*
   * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  package com.sun.max.vm.ext.jvmti;
  
  import static com.sun.max.vm.ext.jvmti.JVMTICallbacks.*;
  import static com.sun.max.vm.ext.jvmti.JVMTIConstants.*;
  import static com.sun.max.vm.ext.jvmti.JVMTIEnvNativeStruct.*;
  import static com.sun.max.vm.ext.jvmti.JVMTIEvents.*;
  import static com.sun.max.vm.ext.jvmti.JVMTIFieldWatch.*;
  import static com.sun.max.vm.intrinsics.MaxineIntrinsicIDs.*;
  import static com.sun.max.unsafe.UnsafeCast.*;
  
  import java.security.*;
  import java.util.*;
  
  import com.oracle.max.vm.ext.t1x.*;
  import com.oracle.max.vm.ext.t1x.jvmti.*;
  import com.sun.cri.bytecode.*;
  import com.sun.max.annotate.*;
  import com.sun.max.unsafe.*;
  import com.sun.max.vm.*;
  import com.sun.max.vm.actor.holder.*;
  import com.sun.max.vm.actor.member.*;
  import com.sun.max.vm.compiler.*;
  import com.sun.max.vm.compiler.target.*;
  import com.sun.max.vm.ext.jvmti.JVMTIUtil.ModeUnion;
  import com.sun.max.vm.ext.jvmti.JVMTIBreakpoints.*;
  import com.sun.max.vm.ext.jvmti.JVMTIException.*;
  import com.sun.max.vm.ext.jvmti.JVMTIThreadFunctions.*;
  import com.sun.max.vm.jni.*;
  import com.sun.max.vm.runtime.*;
  import com.sun.max.vm.thread.*;
  import com.sun.max.vm.ti.*;
  
  /**
   * The heart of the Maxine JVMTI implementation.
   * Handles environments, event handling.
   * Supports both standard (native) agents and the Java agents written to {@link JJVMTI}.
   */
  public class JVMTI {
      /**
       * Holds state associated with this side (i.e. Java) of the implementation that is independent
       * of whether this is a native or a Java agent.
       */
      static abstract class Env {
          /**
           * Object tagging.
           */
          JVMTITags tags = new JVMTITags();
          /**
           * The global event settings for this agent.
           */
          long globalEventSettings;
          /**
           * The per-thread event settings for this agent.
           */
          JVMTIEvents.PerThreadSettings perThreadEventSettings = new JVMTIEvents.PerThreadSettings();
  
          boolean isFree() {
              return false;
          }
  
      }
  
      /**
       * Subclass used for traditional native agents.
       */
      static class NativeEnv extends Env {
          /**
           * The C struct used by the native agents. {@see JVMTIEnvNativeStruct}.
           */
          Pointer cstruct;
  
          /**
           * Support for {@code Get/SetEnvironmentLocalStorage}.
           */
          Pointer envStorage;
  
          /**
           * additions to the boot classpath by this agent.
           */
          long[] bootClassPathAdd = new long[4];
  
          @Override
          boolean isFree() {
              return cstruct.isZero();
          }
  
      }
  
      /**
       * Abstract subclass used for agents that use {@link JJVMTI}.
       */
      public static class JavaEnv extends Env {
          final EnumSet<JVMTICapabilities.E> capabilities = EnumSet.noneOf(JVMTICapabilities.E.class);
          final JJVMTI.EventCallbacks callbackHandler;
          protected JavaEnv(JJVMTI.EventCallbacks callbackHandler) {
              this.callbackHandler = callbackHandler;
          }
  
      }
  
      static class JVMTIHandler extends NullVMTIHandler implements VMTIHandler {
  
          @Override
          public void initialize() {
              JVMTI.initialize();
          }
  
          @Override
          public void vmInitialized() {
              /*
               * JVMTI has a VM_START and a VM_INIT event, but the distinction
               * is minimal and only relates to which JVMTI functions may be called.
               * The gating function for dispatching either event this late
               * is that any JNI function can be called from VM_START, e.g, FindClass,
               * which requires that essentially all of the VM machinery is working.
               */
              JVMTI.event(E.VM_START);
              JVMTI.event(E.VM_INIT);
          }
  
          @Override
          public void vmDeath() {
              JVMTI.event(E.VM_DEATH);
          }
  
          @Override
          public void threadStart(VmThread vmThread) {
              JVMTI.event(E.THREAD_START, vmThread);
          }
  
          @Override
          public void threadEnd(VmThread vmThread) {
              JVMTI.event(E.THREAD_END, vmThread);
          }
  
          @Override
          public boolean classFileLoadHookHandled() {
              return JVMTI.eventNeeded(E.CLASS_FILE_LOAD_HOOK);
          }
  
          @Override
          public byte[] classFileLoadHook(ClassLoader classLoader, String className, ProtectionDomain protectionDomain, byte[] classfileBytes) {
              return JVMTI.classFileLoadHook(classLoader, className, protectionDomain, classfileBytes);
          }
  
          @Override
          public void classLoad(ClassActor classActor) {
              // Have to send both events
              JVMTI.event(E.CLASS_LOAD, classActor);
              JVMTI.event(E.CLASS_PREPARE, classActor);
          }
  
          @Override
          public void methodCompiled(ClassMethodActor classMethodActor) {
              JVMTI.event(E.COMPILED_METHOD_LOAD, classMethodActor);
          }
  
          @Override
          public void methodUnloaded(ClassMethodActor classMethodActor, Pointer codeAddr) {
              MethodUnloadEventData methodUnloadEventData = threadMethodUnloadEventData.get();
              methodUnloadEventData.classMethodActor = classMethodActor;
              methodUnloadEventData.codeAddr = codeAddr;
              JVMTI.event(E.COMPILED_METHOD_UNLOAD, methodUnloadEventData);
          }
  
          @Override
          public boolean hasBreakpoints(ClassMethodActor classMethodActor) {
              return JVMTIBreakpoints.hasBreakpoints(classMethodActor);
          }
  
          @Override
          public String bootclassPathExtension() {
              return JVMTIClassFunctions.getAddedBootClassPath();
          }
  
          @Override
          public void beginUpcallVM() {
              JVMTIVmThreadLocal.setBit(VmThread.currentTLA(), JVMTIVmThreadLocal.IN_UPCALL);
          }
  
          @Override
          public void endUpcallVM() {
              JVMTIVmThreadLocal.unsetBit(VmThread.currentTLA(), JVMTIVmThreadLocal.IN_UPCALL);
          }
  
          @Override
          public void beginGC() {
              JVMTI.event(E.GARBAGE_COLLECTION_START);
          }
  
          @Override
          public void endGC() {
              JVMTI.event(E.GARBAGE_COLLECTION_FINISH);
          }
  
          @Override
          public boolean nativeCallNeedsPrologueAndEpilogue(MethodActor ma) {
              return ma != JVMTIFunctions.currentJniEnv;
          }
  
          @Override
          public void registerAgent(Word agentHandle) {
              JVMTI.setJVMTIEnv(agentHandle);
          }
  
          @Override
          public int activeAgents() {
              return JVMTI.activeEnvCount;
          }
  
          @Override
          public void raise(Throwable throwable, Pointer sp, Pointer fp, CodePointer ip) {
              JVMTIException.raiseEvent(throwable, sp, fp, ip);
          }
  
          @Override
          public RuntimeCompiler runtimeCompiler(RuntimeCompiler stdRuntimeCompiler) {
              return new T1X(JVMTI_T1XTemplateSource.class, new JVMTI_T1XCompilationFactory());
          }
  
          @Override
          public boolean needsVMTICompilation(ClassMethodActor classMethodActor) {
              // N.B. We do not instrument reflection stubs. Amongst other reasons they
              // can be generated by upcalls from JVMTI agents using JNI, e.g. Class.getName,
              // causing runaway recursion.
              ClassActor holder = classMethodActor.holder();
              return !holder.isReflectionStub() && JVMTI.compiledCodeEventsNeeded(classMethodActor);
          }
  
          @Override
          public boolean needsSpecialGetCallerClass() {
              return true;
          }
  
          @Override
          @NEVER_INLINE
          public Class getCallerClassForFindClass(int realFramesToSkip) {
              return JVMTIClassLoader.getCallerClassForFindClass(realFramesToSkip++);
          }
  
      }
  
      static {
          VMTI.registerEventHandler(new JVMTIHandler());
      }
  
      private static final String AGENT_ONLOAD = "Agent_OnLoad";
      private static final String AGENT_ONUNLOAD = "Agent_OnUnLoad";
  
      /**
       * Since the agent initialization code happens very early, before we have a functional heap,
       * we static allocate storage for key data structures. The day that someone tries to runs
       * Maxine with more than {@link MAX_ENVS} agents, we will celebrate -;)
       */
      static final int MAX_ENVS = 8;
      static final int MAX_NATIVE_ENVS = 6;
  
      /**
       * The record of registered agent environments, used to handle callbacks.
       * A free slot is denoted by {@link Env#cstruct} having a zero value.
       */
      static final Env[] jvmtiEnvs;
      /**
       * The {@link #jvmtiEnvs} array is updated indirectly by the {@link #SetJVMTIEnv(Pointer)} upcall
       * during agent initialization. Hence we use a static variable to index the array during initialization.
       * N.B. after initialization this value should not be used to limit the search of the array as
       * agents may come and go.
       */
      private static int nativeEnvsIndex;
  
      /**
       * The number of active agent environments.
       */
      static int activeEnvCount;
  
      /**
       * The phase of execution.
       */
      static int phase = JVMTI_PHASE_ONLOAD;
  
      static {
          jvmtiEnvs = new Env[MAX_ENVS];
          for (int i = 0; i < MAX_NATIVE_ENVS; i++) {
              NativeEnv jvmtiEnv = new NativeEnv();
              jvmtiEnvs[i] = jvmtiEnv;
              for (int j = 0; j < jvmtiEnv.bootClassPathAdd.length; j++) {
                  jvmtiEnv.bootClassPathAdd[j] = 0;
              }
          }
          VMOptions.addFieldOption("-XX:", "JVMTI_VM", JVMTI.class, "Include VM classes in JVMTI results.");
      }
  
      /**
       * Called exactly once during agent startup (strictly speaking during jvmtiEnv creation in jvmti.c)
       * allowing it to be recorded once here to support the handling of callbacks to the agent.
       * @param agentHandle the jvmtienv C struct
       */
      public static void setJVMTIEnv(Word agentHandle) {
          if (nativeEnvsIndex >= MAX_NATIVE_ENVS) {
              Log.println("too many JVMTI agents");
              MaxineVM.native_exit(1);
          }
          NativeEnv nativeEnv = (NativeEnv) jvmtiEnvs[nativeEnvsIndex++];
          nativeEnv.cstruct = agentHandle.asPointer();
          activeEnvCount++;
      }
  
      /**
       * Called to register the environment for a Java JVMTI agent.
       * @param env
       */
      public static synchronized void setJVMTIJavaEnv(Env env) {
          for (int i = MAX_NATIVE_ENVS; i < MAX_ENVS; i++) {
              if (jvmtiEnvs[i] == null) {
                  jvmtiEnvs[i] = env;
                  activeEnvCount++;
                  return;
              }
          }
          Log.println("too many JVMTI agents");
          MaxineVM.native_exit(1);
      }
  
      public static synchronized int disposeJVMTIJavaEnv(Env env) {
          for (int i = MAX_NATIVE_ENVS; i < MAX_ENVS; i++) {
              if (jvmtiEnvs[i] == env) {
                  jvmtiEnvs[i] = null;
                  activeEnvCount--;
                  return JVMTI_ERROR_NONE;
              }
          }
          return JVMTI_ERROR_INVALID_ENVIRONMENT;
      }
  
      static Env getEnv(Pointer env) {
          for (int i = 0; i < MAX_NATIVE_ENVS; i++) {
              NativeEnv nativeEnv = (NativeEnv) jvmtiEnvs[i];
              if (nativeEnv.cstruct.equals(env)) {
                  return jvmtiEnvs[i];
              }
          }
          return null;
      }
  
      static synchronized int disposeEnv(Pointer env) {
          for (int i = 0; i < MAX_NATIVE_ENVS; i++) {
              NativeEnv nativeEnv = (NativeEnv) jvmtiEnvs[i];
              if (nativeEnv.cstruct.equals(env)) {
                  // TODO cleanup
                  nativeEnv.cstruct = Pointer.zero();
                  activeEnvCount--;
                  return JVMTI_ERROR_NONE;
              }
          }
          return JVMTI_ERROR_INVALID_ENVIRONMENT;
      }
  
      /**
       * Initial entry from VM at the start of {@code PRISTINE} phase, i.e., before {code
       * VMConfiguration.initializeSchemes(MaxineVM.Phase.PRISTINE)} has been called. We call Agent_OnLoad for all the
       * agents listed in VM startup command.
       */
      public static void initialize() {
          NativeInterfaces.initFunctionTable(getJVMTIInterface(-1), JVMTIFunctions.jvmtiFunctions, JVMTIFunctions.jvmtiFunctionActors);
          JVMTISystem.initSystemProperties();
  
          for (int i = 0; i < AgentVMOption.count(); i++) {
  