1 /*
   2  * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.code;
  26 
  27 import java.io.*;
  28 import java.util.*;
  29 import sun.jvm.hotspot.debugger.*;
  30 import sun.jvm.hotspot.memory.*;
  31 import sun.jvm.hotspot.oops.*;
  32 import sun.jvm.hotspot.runtime.*;
  33 import sun.jvm.hotspot.types.*;
  34 import sun.jvm.hotspot.utilities.*;
  35 
  36 public class NMethod extends CodeBlob {
  37   private static long          pcDescSize;
  38   private static sun.jvm.hotspot.types.OopField methodField;
  39   /** != InvocationEntryBci if this nmethod is an on-stack replacement method */
  40   private static CIntegerField entryBCIField;
  41   /** To support simple linked-list chaining of nmethods */
  42   private static AddressField  osrLinkField;
  43   private static AddressField  scavengeRootLinkField;
  44   private static JByteField    scavengeRootStateField;
  45 
  46   /** Offsets for different nmethod parts */
  47   private static CIntegerField exceptionOffsetField;
  48   private static CIntegerField deoptOffsetField;
  49   private static CIntegerField deoptMhOffsetField;
  50   private static CIntegerField origPCOffsetField;
  51   private static CIntegerField stubOffsetField;
  52   private static CIntegerField oopsOffsetField;
  53   private static CIntegerField scopesDataOffsetField;
  54   private static CIntegerField scopesPCsOffsetField;
  55   private static CIntegerField dependenciesOffsetField;
  56   private static CIntegerField handlerTableOffsetField;
  57   private static CIntegerField nulChkTableOffsetField;
  58   private static CIntegerField nmethodEndOffsetField;
  59 
  60   /** Offsets for entry points */
  61   /** Entry point with class check */
  62   private static AddressField  entryPointField;
  63   /** Entry point without class check */
  64   private static AddressField  verifiedEntryPointField;
  65   /** Entry point for on stack replacement */
  66   private static AddressField  osrEntryPointField;
  67 
  68   // FIXME: add access to flags (how?)
  69 
  70   /** NMethod Flushing lock (if non-zero, then the nmethod is not removed) */
  71   private static JIntField     lockCountField;
  72 
  73   /** not_entrant method removal. Each mark_sweep pass will update
  74       this mark to current sweep invocation count if it is seen on the
  75       stack.  An not_entrant method can be removed when there is no
  76       more activations, i.e., when the _stack_traversal_mark is less than
  77       current sweep traversal index. */
  78   private static CIntegerField stackTraversalMarkField;
  79 
  80   static {
  81     VM.registerVMInitializedObserver(new Observer() {
  82         public void update(Observable o, Object data) {
  83           initialize(VM.getVM().getTypeDataBase());
  84         }
  85       });
  86   }
  87 
  88   private static void initialize(TypeDataBase db) {
  89     Type type = db.lookupType("nmethod");
  90 
  91     methodField                 = type.getOopField("_method");
  92     entryBCIField               = type.getCIntegerField("_entry_bci");
  93     osrLinkField                = type.getAddressField("_osr_link");
  94     scavengeRootLinkField       = type.getAddressField("_scavenge_root_link");
  95     scavengeRootStateField      = type.getJByteField("_scavenge_root_state");
  96 
  97     exceptionOffsetField        = type.getCIntegerField("_exception_offset");
  98     deoptOffsetField            = type.getCIntegerField("_deoptimize_offset");
  99     deoptMhOffsetField          = type.getCIntegerField("_deoptimize_mh_offset");
 100     origPCOffsetField           = type.getCIntegerField("_orig_pc_offset");
 101     stubOffsetField             = type.getCIntegerField("_stub_offset");
 102     oopsOffsetField             = type.getCIntegerField("_oops_offset");
 103     scopesDataOffsetField       = type.getCIntegerField("_scopes_data_offset");
 104     scopesPCsOffsetField        = type.getCIntegerField("_scopes_pcs_offset");
 105     dependenciesOffsetField     = type.getCIntegerField("_dependencies_offset");
 106     handlerTableOffsetField     = type.getCIntegerField("_handler_table_offset");
 107     nulChkTableOffsetField      = type.getCIntegerField("_nul_chk_table_offset");
 108     nmethodEndOffsetField       = type.getCIntegerField("_nmethod_end_offset");
 109     entryPointField             = type.getAddressField("_entry_point");
 110     verifiedEntryPointField     = type.getAddressField("_verified_entry_point");
 111     osrEntryPointField          = type.getAddressField("_osr_entry_point");
 112     lockCountField              = type.getJIntField("_lock_count");
 113     stackTraversalMarkField     = type.getCIntegerField("_stack_traversal_mark");
 114 
 115     pcDescSize = db.lookupType("PcDesc").getSize();
 116   }
 117 
 118   public NMethod(Address addr) {
 119     super(addr);
 120   }
 121 
 122 
 123   // Accessors
 124   public Address getAddress() {
 125     return addr;
 126   }
 127 
 128   public Method getMethod() {
 129     return (Method) VM.getVM().getObjectHeap().newOop(methodField.getValue(addr));
 130   }
 131 
 132   // Type info
 133   public boolean isNMethod()      { return true;                    }
 134   public boolean isJavaMethod()   { return !getMethod().isNative(); }
 135   public boolean isNativeMethod() { return getMethod().isNative();  }
 136   public boolean isOSRMethod()    { return getEntryBCI() != VM.getVM().getInvocationEntryBCI(); }
 137 
 138   /** Boundaries for different parts */
 139   public Address constantsBegin()       { return contentBegin();                                     }
 140   public Address constantsEnd()         { return getEntryPoint();                                    }
 141   public Address instsBegin()           { return codeBegin();                                        }
 142   public Address instsEnd()             { return headerBegin().addOffsetTo(getStubOffset());         }
 143   public Address exceptionBegin()       { return headerBegin().addOffsetTo(getExceptionOffset());    }
 144   public Address deoptHandlerBegin()    { return headerBegin().addOffsetTo(getDeoptOffset());        }
 145   public Address deoptMhHandlerBegin()  { return headerBegin().addOffsetTo(getDeoptMhOffset());      }
 146   public Address stubBegin()            { return headerBegin().addOffsetTo(getStubOffset());         }
 147   public Address stubEnd()              { return headerBegin().addOffsetTo(getOopsOffset());         }
 148   public Address oopsBegin()            { return headerBegin().addOffsetTo(getOopsOffset());         }
 149   public Address oopsEnd()              { return headerBegin().addOffsetTo(getScopesDataOffset());   }
 150   public Address scopesDataBegin()      { return headerBegin().addOffsetTo(getScopesDataOffset());   }
 151   public Address scopesDataEnd()        { return headerBegin().addOffsetTo(getScopesPCsOffset());    }
 152   public Address scopesPCsBegin()       { return headerBegin().addOffsetTo(getScopesPCsOffset());    }
 153   public Address scopesPCsEnd()         { return headerBegin().addOffsetTo(getDependenciesOffset()); }
 154   public Address dependenciesBegin()    { return headerBegin().addOffsetTo(getDependenciesOffset()); }
 155   public Address dependenciesEnd()      { return headerBegin().addOffsetTo(getHandlerTableOffset()); }
 156   public Address handlerTableBegin()    { return headerBegin().addOffsetTo(getHandlerTableOffset()); }
 157   public Address handlerTableEnd()      { return headerBegin().addOffsetTo(getNulChkTableOffset());  }
 158   public Address nulChkTableBegin()     { return headerBegin().addOffsetTo(getNulChkTableOffset());  }
 159   public Address nulChkTableEnd()       { return headerBegin().addOffsetTo(getNMethodEndOffset());   }
 160 
 161   public int constantsSize()            { return (int) constantsEnd()   .minus(constantsBegin());    }
 162   public int instsSize()                { return (int) instsEnd()       .minus(instsBegin());        }
 163   public int stubSize()                 { return (int) stubEnd()        .minus(stubBegin());         }
 164   public int oopsSize()                 { return (int) oopsEnd()        .minus(oopsBegin());         }
 165   public int scopesDataSize()           { return (int) scopesDataEnd()  .minus(scopesDataBegin());   }
 166   public int scopesPCsSize()            { return (int) scopesPCsEnd()   .minus(scopesPCsBegin());    }
 167   public int dependenciesSize()         { return (int) dependenciesEnd().minus(dependenciesBegin()); }
 168   public int handlerTableSize()         { return (int) handlerTableEnd().minus(handlerTableBegin()); }
 169   public int nulChkTableSize()          { return (int) nulChkTableEnd() .minus(nulChkTableBegin());  }
 170   public int origPCOffset()             { return (int) origPCOffsetField.getValue(addr);             }
 171 
 172   public int totalSize() {
 173     return
 174       constantsSize()    +
 175       instsSize()        +
 176       stubSize()         +
 177       scopesDataSize()   +
 178       scopesPCsSize()    +
 179       dependenciesSize() +
 180       handlerTableSize() +
 181       nulChkTableSize();
 182   }
 183 
 184   public boolean constantsContains   (Address addr) { return constantsBegin()   .lessThanOrEqual(addr) && constantsEnd()   .greaterThan(addr); }
 185   public boolean instsContains       (Address addr) { return instsBegin()       .lessThanOrEqual(addr) && instsEnd()       .greaterThan(addr); }
 186   public boolean stubContains        (Address addr) { return stubBegin()        .lessThanOrEqual(addr) && stubEnd()        .greaterThan(addr); }
 187   public boolean oopsContains        (Address addr) { return oopsBegin()        .lessThanOrEqual(addr) && oopsEnd()        .greaterThan(addr); }
 188   public boolean scopesDataContains  (Address addr) { return scopesDataBegin()  .lessThanOrEqual(addr) && scopesDataEnd()  .greaterThan(addr); }
 189   public boolean scopesPCsContains   (Address addr) { return scopesPCsBegin()   .lessThanOrEqual(addr) && scopesPCsEnd()   .greaterThan(addr); }
 190   public boolean handlerTableContains(Address addr) { return handlerTableBegin().lessThanOrEqual(addr) && handlerTableEnd().greaterThan(addr); }
 191   public boolean nulChkTableContains (Address addr) { return nulChkTableBegin() .lessThanOrEqual(addr) && nulChkTableEnd() .greaterThan(addr); }
 192 
 193   public int getOopsLength() { return (int) (oopsSize() / VM.getVM().getOopSize()); }
 194 
 195   /** Entry points */
 196   public Address getEntryPoint()         { return entryPointField.getValue(addr);         }
 197   public Address getVerifiedEntryPoint() { return verifiedEntryPointField.getValue(addr); }
 198 
 199   /** Support for oops in scopes and relocs. Note: index 0 is reserved for null. */
 200   public OopHandle getOopAt(int index) {
 201     if (index == 0) return null;
 202     if (Assert.ASSERTS_ENABLED) {
 203       Assert.that(index > 0 && index <= getOopsLength(), "must be a valid non-zero index");
 204     }
 205     return oopsBegin().getOopHandleAt((index - 1) * VM.getVM().getOopSize());
 206   }
 207 
 208   // FIXME: add interpreter_entry_point()
 209   // FIXME: add lazy_interpreter_entry_point() for C2
 210 
 211   // **********
 212   // * FIXME: * ADD ACCESS TO FLAGS!!!!
 213   // **********
 214   // public boolean isInUse();
 215   // public boolean isAlive();
 216   // public boolean isNotEntrant();
 217   // public boolean isZombie();
 218 
 219   // ********************************
 220   // * MAJOR FIXME: MAJOR HACK HERE *
 221   // ********************************
 222   public boolean isZombie() { return false; }
 223 
 224   // public boolean isUnloaded();
 225   // public boolean isYoung();
 226   // public boolean isOld();
 227   // public int     age();
 228   // public boolean isMarkedForDeoptimization();
 229   // public boolean isMarkedForUnloading();
 230   // public boolean isMarkedForReclamation();
 231   // public int     level();
 232   // public int     version();
 233 
 234   // FIXME: add mutators for above
 235   // FIXME: add exception cache access?
 236 
 237   /** On-stack replacement support */
 238   // FIXME: add mutators
 239   public int getOSREntryBCI() {
 240     if (Assert.ASSERTS_ENABLED) {
 241       Assert.that(getEntryBCI() != VM.getVM().getInvocationEntryBCI(), "wrong kind of nmethod");
 242     }
 243     return getEntryBCI();
 244   }
 245 
 246   public NMethod getOSRLink() {
 247     return (NMethod) VMObjectFactory.newObject(NMethod.class, osrLinkField.getValue(addr));
 248   }
 249 
 250   public NMethod getScavengeRootLink() {
 251     return (NMethod) VMObjectFactory.newObject(NMethod.class, scavengeRootLinkField.getValue(addr));
 252   }
 253 
 254   public int getScavengeRootState() {
 255     return (int) scavengeRootStateField.getValue(addr);
 256   }
 257 
 258   // MethodHandle
 259   public boolean isMethodHandleReturn(Address returnPc) {
 260     // Hard to read a bit fields from Java and it's only there for performance
 261     // so just go directly to the PCDesc
 262     // if (!hasMethodHandleInvokes())  return false;
 263     PCDesc pd = getPCDescAt(returnPc);
 264     if (pd == null)
 265       return false;
 266     return pd.isMethodHandleInvoke();
 267   }
 268 
 269   // Deopt
 270   // Return true is the PC is one would expect if the frame is being deopted.
 271   public boolean isDeoptPc      (Address pc) { return isDeoptEntry(pc) || isDeoptMhEntry(pc); }
 272   public boolean isDeoptEntry   (Address pc) { return pc == deoptHandlerBegin(); }
 273   public boolean isDeoptMhEntry (Address pc) { return pc == deoptMhHandlerBegin(); }
 274 
 275   /** Tells whether frames described by this nmethod can be
 276       deoptimized. Note: native wrappers cannot be deoptimized. */
 277   public boolean canBeDeoptimized() { return isJavaMethod(); }
 278 
 279   // FIXME: add inline cache support
 280   // FIXME: add flush()
 281 
 282   public boolean isLockedByVM() { return lockCountField.getValue(addr) > 0; }
 283 
 284   // FIXME: add mark_as_seen_on_stack
 285   // FIXME: add can_not_entrant_be_converted
 286 
 287   // FIXME: add GC support
 288   //  void follow_roots_or_mark_for_unloading(bool unloading_occurred, bool& marked_for_unloading);
 289   //  void follow_root_or_mark_for_unloading(oop* root, bool unloading_occurred, bool& marked_for_unloading);
 290   //  void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, void f(oop*));
 291   //  void adjust_pointers();
 292 
 293   /** Finds a PCDesc with real-pc equal to "pc" */
 294   public PCDesc getPCDescAt(Address pc) {
 295     // FIXME: consider adding cache like the one down in the VM
 296     for (Address p = scopesPCsBegin(); p.lessThan(scopesPCsEnd()); p = p.addOffsetTo(pcDescSize)) {
 297       PCDesc pcDesc = new PCDesc(p);
 298       if (pcDesc.getRealPC(this).equals(pc)) {
 299         return pcDesc;
 300       }
 301     }
 302     return null;
 303   }
 304 
 305   /** ScopeDesc for an instruction */
 306   public ScopeDesc getScopeDescAt(Address pc) {
 307     PCDesc pd = getPCDescAt(pc);
 308     if (Assert.ASSERTS_ENABLED) {
 309       Assert.that(pd != null, "scope must be present");
 310     }
 311     return new ScopeDesc(this, pd.getScopeDecodeOffset(), pd.getObjDecodeOffset(), pd.getReexecute());
 312   }
 313 
 314   /** This is only for use by the debugging system, and is only
 315       intended for use in the topmost frame, where we are not
 316       guaranteed to be at a PC for which we have a PCDesc. It finds
 317       the PCDesc with realPC closest to the current PC. */
 318   public PCDesc getPCDescNearDbg(Address pc) {
 319     PCDesc bestGuessPCDesc = null;
 320     long bestDistance = 0;
 321     for (Address p = scopesPCsBegin(); p.lessThan(scopesPCsEnd()); p = p.addOffsetTo(pcDescSize)) {
 322       PCDesc pcDesc = new PCDesc(p);
 323       // In case pc is null
 324       long distance = -pcDesc.getRealPC(this).minus(pc);
 325       if ((bestGuessPCDesc == null) ||
 326           ((distance >= 0) && (distance < bestDistance))) {
 327         bestGuessPCDesc = pcDesc;
 328         bestDistance    = distance;
 329       }
 330     }
 331     return bestGuessPCDesc;
 332   }
 333 
 334   /** This is only for use by the debugging system, and is only
 335       intended for use in the topmost frame, where we are not
 336       guaranteed to be at a PC for which we have a PCDesc. It finds
 337       the ScopeDesc closest to the current PC. NOTE that this may
 338       return NULL for compiled methods which don't have any
 339       ScopeDescs! */
 340   public ScopeDesc getScopeDescNearDbg(Address pc) {
 341     PCDesc pd = getPCDescNearDbg(pc);
 342     if (pd == null) return null;
 343     return new ScopeDesc(this, pd.getScopeDecodeOffset(), pd.getObjDecodeOffset(), pd.getReexecute());
 344   }
 345 
 346   public Map/*<Address, PCDesc>*/ getSafepoints() {
 347     Map safepoints = new HashMap(); // Map<Address, PCDesc>
 348     sun.jvm.hotspot.debugger.Address p = null;
 349     for (p = scopesPCsBegin(); p.lessThan(scopesPCsEnd());
 350          p = p.addOffsetTo(pcDescSize)) {
 351        PCDesc pcDesc = new PCDesc(p);
 352        sun.jvm.hotspot.debugger.Address pc = pcDesc.getRealPC(this);
 353        safepoints.put(pc, pcDesc);
 354     }
 355     return safepoints;
 356   }
 357 
 358   // FIXME: add getPCOffsetForBCI()
 359   // FIXME: add embeddedOopAt()
 360   // FIXME: add isDependentOn()
 361   // FIXME: add isPatchableAt()
 362 
 363   /** Support for code generation. Only here for proof-of-concept. */
 364   public static int getEntryPointOffset()            { return (int) entryPointField.getOffset();            }
 365   public static int getVerifiedEntryPointOffset()    { return (int) verifiedEntryPointField.getOffset();    }
 366   public static int getOSREntryPointOffset()         { return (int) osrEntryPointField.getOffset();         }
 367   public static int getEntryBCIOffset()              { return (int) entryBCIField.getOffset();              }
 368   /** NOTE: renamed from "method_offset_in_bytes" */
 369   public static int getMethodOffset()                { return (int) methodField.getOffset();                }
 370 
 371   public void print() {
 372     printOn(System.out);
 373   }
 374 
 375   protected void printComponentsOn(PrintStream tty) {
 376     // FIXME: add relocation information
 377     tty.println(" content: [" + contentBegin() + ", " + contentEnd() + "), " +
 378                 " code: [" + codeBegin() + ", " + codeEnd() + "), " +
 379                 " data: [" + dataBegin() + ", " + dataEnd() + "), " +
 380                 " oops: [" + oopsBegin() + ", " + oopsEnd() + "), " +
 381                 " frame size: " + getFrameSize());
 382   }
 383 
 384   public String toString() {
 385     Method method = getMethod();
 386     return "NMethod for " +
 387             method.getMethodHolder().getName().asString() + "." +
 388             method.getName().asString() + method.getSignature().asString() + "==>n" +
 389             super.toString();
 390   }
 391 
 392   public String flagsToString() {
 393     // FIXME need access to flags...
 394     return "";
 395   }
 396 
 397   public String getName() {
 398     Method method = getMethod();
 399     return "NMethod for " +
 400            method.getMethodHolder().getName().asString() + "." +
 401            method.getName().asString() +
 402            method.getSignature().asString();
 403   }
 404 
 405   //--------------------------------------------------------------------------------
 406   // Internals only below this point
 407   //
 408 
 409   private int getEntryBCI()           { return (int) entryBCIField          .getValue(addr); }
 410   private int getExceptionOffset()    { return (int) exceptionOffsetField   .getValue(addr); }
 411   private int getDeoptOffset()        { return (int) deoptOffsetField       .getValue(addr); }
 412   private int getDeoptMhOffset()      { return (int) deoptMhOffsetField     .getValue(addr); }
 413   private int getStubOffset()         { return (int) stubOffsetField        .getValue(addr); }
 414   private int getOopsOffset()         { return (int) oopsOffsetField        .getValue(addr); }
 415   private int getScopesDataOffset()   { return (int) scopesDataOffsetField  .getValue(addr); }
 416   private int getScopesPCsOffset()    { return (int) scopesPCsOffsetField   .getValue(addr); }
 417   private int getDependenciesOffset() { return (int) dependenciesOffsetField.getValue(addr); }
 418   private int getHandlerTableOffset() { return (int) handlerTableOffsetField.getValue(addr); }
 419   private int getNulChkTableOffset()  { return (int) nulChkTableOffsetField .getValue(addr); }
 420   private int getNMethodEndOffset()   { return (int) nmethodEndOffsetField  .getValue(addr); }
 421 }