1 /*
   2  * Copyright 2000-2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any 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 CIntegerField zombieInstructionSizeField;
  39   private static sun.jvm.hotspot.types.OopField methodField;
  40   /** != InvocationEntryBci if this nmethod is an on-stack replacement method */
  41   private static CIntegerField entryBCIField;
  42   /** To support simple linked-list chaining of nmethods */
  43   private static AddressField  linkField;
  44   /** Offsets for different nmethod parts */
  45   private static CIntegerField exceptionOffsetField;
  46   private static CIntegerField deoptOffsetField;
  47   private static CIntegerField origPCOffsetField;
  48   private static CIntegerField stubOffsetField;
  49   private static CIntegerField scopesDataOffsetField;
  50   private static CIntegerField scopesPCsOffsetField;
  51   private static CIntegerField dependenciesOffsetField;
  52   private static CIntegerField handlerTableOffsetField;
  53   private static CIntegerField nulChkTableOffsetField;
  54   private static CIntegerField nmethodEndOffsetField;
  55 
  56   /** Offsets for entry points */
  57   /** Entry point with class check */
  58   private static AddressField  entryPointField;
  59   /** Entry point without class check */
  60   private static AddressField  verifiedEntryPointField;
  61   /** Entry point for on stack replacement */
  62   private static AddressField  osrEntryPointField;
  63 
  64   // FIXME: add access to flags (how?)
  65 
  66   /** NMethod Flushing lock (if non-zero, then the nmethod is not removed) */
  67   private static JIntField     lockCountField;
  68 
  69   /** not_entrant method removal. Each mark_sweep pass will update
  70       this mark to current sweep invocation count if it is seen on the
  71       stack.  An not_entrant method can be removed when there is no
  72       more activations, i.e., when the _stack_traversal_mark is less than
  73       current sweep traversal index. */
  74   private static CIntegerField stackTraversalMarkField;
  75 
  76   static {
  77     VM.registerVMInitializedObserver(new Observer() {
  78         public void update(Observable o, Object data) {
  79           initialize(VM.getVM().getTypeDataBase());
  80         }
  81       });
  82   }
  83 
  84   private static void initialize(TypeDataBase db) {
  85     Type type = db.lookupType("nmethod");
  86 
  87     zombieInstructionSizeField  = type.getCIntegerField("_zombie_instruction_size");
  88     methodField                 = type.getOopField("_method");
  89     entryBCIField               = type.getCIntegerField("_entry_bci");
  90     linkField                   = type.getAddressField("_link");
  91     exceptionOffsetField        = type.getCIntegerField("_exception_offset");
  92     deoptOffsetField            = type.getCIntegerField("_deoptimize_offset");
  93     origPCOffsetField           = type.getCIntegerField("_orig_pc_offset");
  94     stubOffsetField             = type.getCIntegerField("_stub_offset");
  95     scopesDataOffsetField       = type.getCIntegerField("_scopes_data_offset");
  96     scopesPCsOffsetField        = type.getCIntegerField("_scopes_pcs_offset");
  97     dependenciesOffsetField     = type.getCIntegerField("_dependencies_offset");
  98     handlerTableOffsetField     = type.getCIntegerField("_handler_table_offset");
  99     nulChkTableOffsetField      = type.getCIntegerField("_nul_chk_table_offset");
 100     nmethodEndOffsetField       = type.getCIntegerField("_nmethod_end_offset");
 101     entryPointField             = type.getAddressField("_entry_point");
 102     verifiedEntryPointField     = type.getAddressField("_verified_entry_point");
 103     osrEntryPointField          = type.getAddressField("_osr_entry_point");
 104     lockCountField              = type.getJIntField("_lock_count");
 105     stackTraversalMarkField     = type.getCIntegerField("_stack_traversal_mark");
 106 
 107     pcDescSize = db.lookupType("PcDesc").getSize();
 108   }
 109 
 110   public NMethod(Address addr) {
 111     super(addr);
 112   }
 113 
 114 
 115   // Accessors
 116   public Address getAddress() {
 117     return addr;
 118   }
 119 
 120   public Method getMethod() {
 121     return (Method) VM.getVM().getObjectHeap().newOop(methodField.getValue(addr));
 122   }
 123 
 124   // Type info
 125   public boolean isNMethod()      { return true;                    }
 126   public boolean isJavaMethod()   { return !getMethod().isNative(); }
 127   public boolean isNativeMethod() { return getMethod().isNative();  }
 128   public boolean isOSRMethod()    { return getEntryBCI() != VM.getVM().getInvocationEntryBCI(); }
 129 
 130   /** Boundaries for different parts */
 131   public Address constantsBegin()       { return instructionsBegin();                                }
 132   public Address constantsEnd()         { return getEntryPoint();                                    }
 133   public Address codeBegin()            { return getEntryPoint();                                    }
 134   public Address codeEnd()              { return headerBegin().addOffsetTo(getStubOffset());         }
 135   public Address exceptionBegin()       { return headerBegin().addOffsetTo(getExceptionOffset());    }
 136   public Address deoptBegin()           { return headerBegin().addOffsetTo(getDeoptOffset());        }
 137   public Address stubBegin()            { return headerBegin().addOffsetTo(getStubOffset());         }
 138   public Address stubEnd()              { return headerBegin().addOffsetTo(getScopesDataOffset());   }
 139   public Address scopesDataBegin()      { return headerBegin().addOffsetTo(getScopesDataOffset());   }
 140   public Address scopesDataEnd()        { return headerBegin().addOffsetTo(getScopesPCsOffset());    }
 141   public Address scopesPCsBegin()       { return headerBegin().addOffsetTo(getScopesPCsOffset());    }
 142   public Address scopesPCsEnd()         { return headerBegin().addOffsetTo(getDependenciesOffset()); }
 143   public Address dependenciesBegin()    { return headerBegin().addOffsetTo(getDependenciesOffset()); }
 144   public Address dependenciesEnd()      { return headerBegin().addOffsetTo(getHandlerTableOffset()); }
 145   public Address handlerTableBegin()    { return headerBegin().addOffsetTo(getHandlerTableOffset()); }
 146   public Address handlerTableEnd()      { return headerBegin().addOffsetTo(getNulChkTableOffset());  }
 147   public Address nulChkTableBegin()     { return headerBegin().addOffsetTo(getNulChkTableOffset());  }
 148   public Address nulChkTableEnd()       { return headerBegin().addOffsetTo(getNMethodEndOffset());   }
 149 
 150   public int constantsSize()            { return (int) constantsEnd()   .minus(constantsBegin());    }
 151   public int codeSize()                 { return (int) codeEnd()        .minus(codeBegin());         }
 152   public int stubSize()                 { return (int) stubEnd()        .minus(stubBegin());         }
 153   public int scopesDataSize()           { return (int) scopesDataEnd()  .minus(scopesDataBegin());   }
 154   public int scopesPCsSize()            { return (int) scopesPCsEnd()   .minus(scopesPCsBegin());    }
 155   public int dependenciesSize()         { return (int) dependenciesEnd().minus(dependenciesBegin()); }
 156   public int handlerTableSize()         { return (int) handlerTableEnd().minus(handlerTableBegin()); }
 157   public int nulChkTableSize()          { return (int) nulChkTableEnd() .minus(nulChkTableBegin());  }
 158   public int origPCOffset()             { return (int) origPCOffsetField.getValue(addr);             }
 159 
 160   public int totalSize() {
 161     return
 162       constantsSize()    +
 163       codeSize()         +
 164       stubSize()         +
 165       scopesDataSize()   +
 166       scopesPCsSize()    +
 167       dependenciesSize() +
 168       handlerTableSize() +
 169       nulChkTableSize();
 170   }
 171 
 172   public boolean constantsContains   (Address addr) { return constantsBegin()   .lessThanOrEqual(addr) && constantsEnd()   .greaterThan(addr); }
 173   public boolean codeContains        (Address addr) { return codeBegin()        .lessThanOrEqual(addr) && codeEnd()        .greaterThan(addr); }
 174   public boolean stubContains        (Address addr) { return stubBegin()        .lessThanOrEqual(addr) && stubEnd()        .greaterThan(addr); }
 175   public boolean scopesDataContains  (Address addr) { return scopesDataBegin()  .lessThanOrEqual(addr) && scopesDataEnd()  .greaterThan(addr); }
 176   public boolean scopesPCsContains   (Address addr) { return scopesPCsBegin()   .lessThanOrEqual(addr) && scopesPCsEnd()   .greaterThan(addr); }
 177   public boolean handlerTableContains(Address addr) { return handlerTableBegin().lessThanOrEqual(addr) && handlerTableEnd().greaterThan(addr); }
 178   public boolean nulChkTableContains (Address addr) { return nulChkTableBegin() .lessThanOrEqual(addr) && nulChkTableEnd() .greaterThan(addr); }
 179 
 180   /** Entry points */
 181   public Address getEntryPoint()         { return entryPointField.getValue(addr);         }
 182   public Address getVerifiedEntryPoint() { return verifiedEntryPointField.getValue(addr); }
 183 
 184   // FIXME: add interpreter_entry_point()
 185   // FIXME: add lazy_interpreter_entry_point() for C2
 186 
 187   // **********
 188   // * FIXME: * ADD ACCESS TO FLAGS!!!!
 189   // **********
 190   // public boolean isInUse();
 191   // public boolean isAlive();
 192   // public boolean isNotEntrant();
 193   // public boolean isZombie();
 194 
 195   // ********************************
 196   // * MAJOR FIXME: MAJOR HACK HERE *
 197   // ********************************
 198   public boolean isZombie() { return false; }
 199 
 200   // public boolean isUnloaded();
 201   // public boolean isYoung();
 202   // public boolean isOld();
 203   // public int     age();
 204   // public boolean isMarkedForDeoptimization();
 205   // public boolean isMarkedForUnloading();
 206   // public boolean isMarkedForReclamation();
 207   // public int     level();
 208   // public int     version();
 209 
 210   // FIXME: add mutators for above
 211   // FIXME: add exception cache access?
 212 
 213   /** On-stack replacement support */
 214   // FIXME: add mutators
 215   public int getOSREntryBCI() {
 216     if (Assert.ASSERTS_ENABLED) {
 217       Assert.that(getEntryBCI() != VM.getVM().getInvocationEntryBCI(), "wrong kind of nmethod");
 218     }
 219     return getEntryBCI();
 220   }
 221 
 222   public NMethod getLink() {
 223     return (NMethod) VMObjectFactory.newObject(NMethod.class, linkField.getValue(addr));
 224   }
 225 
 226   /** Tells whether frames described by this nmethod can be
 227       deoptimized. Note: native wrappers cannot be deoptimized. */
 228   public boolean canBeDeoptimized() { return isJavaMethod(); }
 229 
 230   // FIXME: add inline cache support
 231   // FIXME: add flush()
 232 
 233   public boolean isLockedByVM() { return lockCountField.getValue(addr) > 0; }
 234 
 235   // FIXME: add mark_as_seen_on_stack
 236   // FIXME: add can_not_entrant_be_converted
 237 
 238   // FIXME: add GC support
 239   //  void follow_roots_or_mark_for_unloading(bool unloading_occurred, bool& marked_for_unloading);
 240   //  void follow_root_or_mark_for_unloading(oop* root, bool unloading_occurred, bool& marked_for_unloading);
 241   //  void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, void f(oop*));
 242   //  void adjust_pointers();
 243 
 244   /** Finds a PCDesc with real-pc equal to "pc" */
 245   public PCDesc getPCDescAt(Address pc) {
 246     // FIXME: consider adding cache like the one down in the VM
 247     for (Address p = scopesPCsBegin(); p.lessThan(scopesPCsEnd()); p = p.addOffsetTo(pcDescSize)) {
 248       PCDesc pcDesc = new PCDesc(p);
 249       if (pcDesc.getRealPC(this).equals(pc)) {
 250         return pcDesc;
 251       }
 252     }
 253     return null;
 254   }
 255 
 256   /** ScopeDesc for an instruction */
 257   public ScopeDesc getScopeDescAt(Address pc) {
 258     PCDesc pd = getPCDescAt(pc);
 259     if (Assert.ASSERTS_ENABLED) {
 260       Assert.that(pd != null, "scope must be present");
 261     }
 262     return new ScopeDesc(this, pd.getScopeDecodeOffset(), pd.getReexecute());
 263   }
 264 
 265   /** This is only for use by the debugging system, and is only
 266       intended for use in the topmost frame, where we are not
 267       guaranteed to be at a PC for which we have a PCDesc. It finds
 268       the PCDesc with realPC closest to the current PC. */
 269   public PCDesc getPCDescNearDbg(Address pc) {
 270     PCDesc bestGuessPCDesc = null;
 271     long bestDistance = 0;
 272     for (Address p = scopesPCsBegin(); p.lessThan(scopesPCsEnd()); p = p.addOffsetTo(pcDescSize)) {
 273       PCDesc pcDesc = new PCDesc(p);
 274       // In case pc is null
 275       long distance = -pcDesc.getRealPC(this).minus(pc);
 276       if ((bestGuessPCDesc == null) ||
 277           ((distance >= 0) && (distance < bestDistance))) {
 278         bestGuessPCDesc = pcDesc;
 279         bestDistance    = distance;
 280       }
 281     }
 282     return bestGuessPCDesc;
 283   }
 284 
 285    PCDesc find_pc_desc(long pc, boolean approximate) {
 286       return find_pc_desc_internal(pc, approximate);
 287    }
 288 
 289    // Finds a PcDesc with real-pc equal to "pc"
 290    PCDesc find_pc_desc_internal(long pc, boolean approximate) {
 291       long base_address = VM.getAddressValue(instructionsBegin());
 292       int pc_offset = (int) (pc - base_address);
 293 
 294       // Fallback algorithm: quasi-linear search for the PcDesc
 295       // Find the last pc_offset less than the given offset.
 296       // The successor must be the required match, if there is a match at all.
 297       // (Use a fixed radix to avoid expensive affine pointer arithmetic.)
 298       Address lower = scopesPCsBegin();
 299       Address upper = scopesPCsEnd();
 300       upper = upper.addOffsetTo(-pcDescSize); // exclude final sentinel
 301       if (lower.greaterThan(upper))  return null;  // native method; no PcDescs at all
 302 
 303       // Take giant steps at first (4096, then 256, then 16, then 1)
 304       int LOG2_RADIX = 4;
 305       int RADIX = (1 << LOG2_RADIX);
 306       Address mid;
 307       for (int step = (1 << (LOG2_RADIX*3)); step > 1; step >>= LOG2_RADIX) {
 308          while ((mid = lower.addOffsetTo(step * pcDescSize)).lessThan(upper)) {
 309             PCDesc m = new PCDesc(mid);
 310             if (m.getPCOffset() < pc_offset) {
 311                lower = mid;
 312             } else {
 313                upper = mid;
 314                break;
 315             }
 316          }
 317       }
 318 
 319       // Sneak up on the value with a linear search of length ~16.
 320       while (true) {
 321          mid = lower.addOffsetTo(pcDescSize);
 322          PCDesc m = new PCDesc(mid);
 323          if (m.getPCOffset() < pc_offset) {
 324             lower = mid;
 325          } else {
 326             upper = mid;
 327             break;
 328          }
 329       }
 330 
 331       PCDesc u = new PCDesc(upper);
 332       if (match_desc(u, pc_offset, approximate)) {
 333          return u;
 334       } else {
 335          return null;
 336       }
 337    }
 338 
 339 
 340    // ScopeDesc retrieval operation
 341    PCDesc pc_desc_at(long pc)   { return find_pc_desc(pc, false); }
 342    // pc_desc_near returns the first PCDesc at or after the givne pc.
 343    PCDesc pc_desc_near(long pc) { return find_pc_desc(pc, true); }
 344 
 345    // Return a the last scope in (begin..end]
 346    public ScopeDesc scope_desc_in(long begin, long end) {
 347       PCDesc p = pc_desc_near(begin+1);
 348       if (p != null && VM.getAddressValue(p.getRealPC(this)) <= end) {
 349          return new ScopeDesc(this, p.getScopeDecodeOffset(), p.getReexecute());
 350       }
 351       return null;
 352    }
 353 
 354    static boolean match_desc(PCDesc pc, int pc_offset, boolean approximate) {
 355       if (!approximate)
 356          return pc.getPCOffset() == pc_offset;
 357       else {
 358          PCDesc prev = new PCDesc(pc.getAddress().addOffsetTo(-pcDescSize));
 359          return prev.getPCOffset() < pc_offset && pc_offset <= pc.getPCOffset();
 360       }
 361    }
 362 
 363   /** This is only for use by the debugging system, and is only
 364       intended for use in the topmost frame, where we are not
 365       guaranteed to be at a PC for which we have a PCDesc. It finds
 366       the ScopeDesc closest to the current PC. NOTE that this may
 367       return NULL for compiled methods which don't have any
 368       ScopeDescs! */
 369   public ScopeDesc getScopeDescNearDbg(Address pc) {
 370     PCDesc pd = getPCDescNearDbg(pc);
 371     if (pd == null) return null;
 372     return new ScopeDesc(this, pd.getScopeDecodeOffset(), pd.getReexecute());
 373   }
 374 
 375   public Map/*<Address, PCDesc>*/ getSafepoints() {
 376     Map safepoints = new HashMap(); // Map<Address, PCDesc>
 377     sun.jvm.hotspot.debugger.Address p = null;
 378     for (p = scopesPCsBegin(); p.lessThan(scopesPCsEnd());
 379          p = p.addOffsetTo(pcDescSize)) {
 380        PCDesc pcDesc = new PCDesc(p);
 381        sun.jvm.hotspot.debugger.Address pc = pcDesc.getRealPC(this);
 382        safepoints.put(pc, pcDesc);
 383     }
 384     return safepoints;
 385   }
 386 
 387   // FIXME: add getPCOffsetForBCI()
 388   // FIXME: add embeddedOopAt()
 389   // FIXME: add isDependentOn()
 390   // FIXME: add isPatchableAt()
 391 
 392   /** Support for code generation. Only here for proof-of-concept. */
 393   public static int getEntryPointOffset()            { return (int) entryPointField.getOffset();            }
 394   public static int getVerifiedEntryPointOffset()    { return (int) verifiedEntryPointField.getOffset();    }
 395   public static int getOSREntryPointOffset()         { return (int) osrEntryPointField.getOffset();         }
 396   public static int getEntryBCIOffset()              { return (int) entryBCIField.getOffset();              }
 397   /** NOTE: renamed from "method_offset_in_bytes" */
 398   public static int getMethodOffset()                { return (int) methodField.getOffset();                }
 399 
 400   public void print() {
 401     printOn(System.out);
 402   }
 403 
 404   public String toString() {
 405     Method method = getMethod();
 406     return "NMethod for " +
 407             method.getMethodHolder().getName().asString() + "." +
 408             method.getName().asString() + method.getSignature().asString() + "==>n" +
 409             super.toString();
 410   }
 411 
 412   public String flagsToString() {
 413     // FIXME need access to flags...
 414     return "";
 415   }
 416 
 417   public String getName() {
 418     Method method = getMethod();
 419     return "NMethod for " +
 420            method.getMethodHolder().getName().asString() + "." +
 421            method.getName().asString() +
 422            method.getSignature().asString();
 423   }
 424 
 425   //--------------------------------------------------------------------------------
 426   // Internals only below this point
 427   //
 428 
 429   private int getEntryBCI()           { return (int) entryBCIField          .getValue(addr); }
 430   private int getExceptionOffset()    { return (int) exceptionOffsetField   .getValue(addr); }
 431   private int getDeoptOffset()        { return (int) deoptOffsetField       .getValue(addr); }
 432   private int getStubOffset()         { return (int) stubOffsetField        .getValue(addr); }
 433   private int getScopesDataOffset()   { return (int) scopesDataOffsetField  .getValue(addr); }
 434   private int getScopesPCsOffset()    { return (int) scopesPCsOffsetField   .getValue(addr); }
 435   private int getDependenciesOffset() { return (int) dependenciesOffsetField.getValue(addr); }
 436   private int getHandlerTableOffset() { return (int) handlerTableOffsetField.getValue(addr); }
 437   private int getNulChkTableOffset()  { return (int) nulChkTableOffsetField .getValue(addr); }
 438   private int getNMethodEndOffset()   { return (int) nmethodEndOffsetField  .getValue(addr); }
 439 }
--- EOF ---