001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.mm.mmtk;
014    
015    import org.jikesrvm.ArchitectureSpecific;
016    import org.jikesrvm.VM;
017    import org.jikesrvm.Services;
018    import org.jikesrvm.classloader.MemberReference;
019    import org.jikesrvm.classloader.RVMMethod;
020    import org.jikesrvm.classloader.RVMType;
021    import org.jikesrvm.compilers.baseline.BaselineCompiledMethod;
022    import org.jikesrvm.compilers.common.CompiledMethod;
023    import org.jikesrvm.compilers.common.CompiledMethods;
024    import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod;
025    import org.jikesrvm.compilers.opt.runtimesupport.OptEncodedCallSiteTree;
026    import org.jikesrvm.compilers.opt.runtimesupport.OptMachineCodeMap;
027    import org.jikesrvm.objectmodel.MiscHeader;
028    import org.jikesrvm.objectmodel.ObjectModel;
029    import org.jikesrvm.objectmodel.TIB;
030    import org.jikesrvm.runtime.Magic;
031    import org.jikesrvm.scheduler.RVMThread;
032    import org.vmmagic.pragma.Inline;
033    import org.vmmagic.pragma.Interruptible;
034    import org.vmmagic.pragma.NoInline;
035    import org.vmmagic.pragma.Uninterruptible;
036    import org.vmmagic.unboxed.Address;
037    import org.vmmagic.unboxed.ObjectReference;
038    import org.vmmagic.unboxed.Offset;
039    import org.vmmagic.unboxed.Word;
040    
041    /**
042     * Class that supports scanning Objects or Arrays for references
043     * during tracing, handling those references, and computing death times
044     */
045    @Uninterruptible public final class TraceInterface extends org.mmtk.vm.TraceInterface implements ArchitectureSpecific.ArchConstants {
046    
047      /***********************************************************************
048       *
049       * Class variables
050       */
051      private static byte[][] allocCallMethods;
052    
053      static {
054        /* Build the list of "own methods" */
055        allocCallMethods = new byte[13][];
056        allocCallMethods[0] = "postAlloc".getBytes();
057        allocCallMethods[1] = "traceAlloc".getBytes();
058        allocCallMethods[2] = "allocateScalar".getBytes();
059        allocCallMethods[3] = "allocateArray".getBytes();
060        allocCallMethods[4] = "clone".getBytes();
061        allocCallMethods[5] = "alloc".getBytes();
062        allocCallMethods[6] = "buildMultiDimensionalArray".getBytes();
063        allocCallMethods[7] = "resolvedNewScalar".getBytes();
064        allocCallMethods[8] = "resolvedNewArray".getBytes();
065        allocCallMethods[9] = "unresolvedNewScalar".getBytes();
066        allocCallMethods[10] = "unresolvedNewArray".getBytes();
067        allocCallMethods[11] = "cloneScalar".getBytes();
068        allocCallMethods[12] = "cloneArray".getBytes();
069      }
070    
071      /***********************************************************************
072       *
073       * Public Methods
074       */
075    
076      /**
077       * Returns if the VM is ready for a garbage collection.
078       *
079       * @return True if the RVM is ready for GC, false otherwise.
080       */
081      public boolean gcEnabled() {
082        return RVMThread.gcEnabled();
083      }
084    
085      /**
086       * Given a method name, determine if it is a "real" method or one
087       * used for allocation/tracing.
088       *
089       * @param name The method name to test as an array of bytes
090       * @return True if the method is a "real" method, false otherwise.
091       */
092      private boolean isAllocCall(byte[] name) {
093        for (int i = 0; i < allocCallMethods.length; i++) {
094          byte[] funcName = Services.getArrayNoBarrier(allocCallMethods, i);
095          if (Magic.getArrayLength(name) == Magic.getArrayLength(funcName)) {
096            /* Compare the letters in the allocCallMethod */
097            int j = Magic.getArrayLength(funcName) - 1;
098            while (j >= 0) {
099              if (Services.getArrayNoBarrier(name, j) !=
100                  Services.getArrayNoBarrier(funcName, j))
101                break;
102              j--;
103            }
104            if (j == -1)
105              return true;
106          }
107        }
108        return false;
109      }
110    
111      /**
112       * This adjusts the offset into an object to reflect what it would look like
113       * if the fields were laid out in memory space immediately after the object
114       * pointer.
115       *
116       * @param isScalar If this is a pointer store to a scalar object
117       * @param src The address of the source object
118       * @param slot The address within <code>src</code> into which
119       * the update will be stored
120       * @return The easy to understand offset of the slot
121       */
122      public Offset adjustSlotOffset(boolean isScalar,
123                                                  ObjectReference src,
124                                                  Address slot) {
125        /* Offset scalar objects so that the fields appear to begin at offset 0
126           of the object. */
127        Offset offset = slot.diff(src.toAddress());
128        if (isScalar)
129          return offset.minus(getHeaderEndOffset());
130        else
131          return offset;
132      }
133    
134      /**
135       * This skips over the frames added by the tracing algorithm, outputs
136       * information identifying the method the containts the "new" call triggering
137       * the allocation, and returns the address of the first non-trace, non-alloc
138       * stack frame.
139       *
140       * @param typeRef The type reference (tib) of the object just allocated
141       * @return The frame pointer address for the method that allocated the object
142       */
143      @NoInline
144      @Interruptible // This can't be uninterruptible --- it is an IO routine
145      public Address skipOwnFramesAndDump(ObjectReference typeRef) {
146        TIB tib = Magic.addressAsTIB(typeRef.toAddress());
147        RVMMethod m = null;
148        int bci = -1;
149        int compiledMethodID = 0;
150        Offset ipOffset = Offset.zero();
151        Address fp = Magic.getFramePointer();
152        Address ip = Magic.getReturnAddress(fp);
153        fp = Magic.getCallerFramePointer(fp);
154        // This code borrows heavily from RVMThread.dumpStack
155        while (Magic.getCallerFramePointer(fp).NE(STACKFRAME_SENTINEL_FP)) {
156          compiledMethodID = Magic.getCompiledMethodID(fp);
157          if (compiledMethodID != INVISIBLE_METHOD_ID) {
158            // normal java frame(s)
159            CompiledMethod compiledMethod =
160              CompiledMethods.getCompiledMethod(compiledMethodID);
161            if (compiledMethod.getCompilerType() != CompiledMethod.TRAP) {
162              ipOffset = compiledMethod.getInstructionOffset(ip);
163              m = compiledMethod.getMethod();
164              if (VM.BuildForOptCompiler && compiledMethod.getCompilerType() == CompiledMethod.OPT) {
165                OptCompiledMethod optInfo = (OptCompiledMethod)compiledMethod;
166                /* Opt stack frames may contain multiple inlined methods. */
167                OptMachineCodeMap map = optInfo.getMCMap();
168                int iei = map.getInlineEncodingForMCOffset(ipOffset);
169                if (iei >= 0) {
170                  int[] inlineEncoding = map.inlineEncoding;
171                  boolean allocCall = true;
172                  bci = map.getBytecodeIndexForMCOffset(ipOffset);
173                  for (int j = iei; j >= 0 && allocCall;
174                       j = OptEncodedCallSiteTree.getParent(j,inlineEncoding)) {
175                    int mid = OptEncodedCallSiteTree.getMethodID(j, inlineEncoding);
176                    m = MemberReference.getMemberRef(mid).asMethodReference().getResolvedMember();
177                    if (!isAllocCall(m.getName().getBytes()))
178                      allocCall = false;
179                    if (j > 0)
180                      bci = OptEncodedCallSiteTree.getByteCodeOffset(j,
181                                                                        inlineEncoding);
182                  }
183                  if (!allocCall)
184                    break;
185                }
186              } else {
187                if (!isAllocCall(m.getName().getBytes())) {
188                  BaselineCompiledMethod baseInfo =
189                    (BaselineCompiledMethod)compiledMethod;
190                  bci = baseInfo.findBytecodeIndexForInstruction(ipOffset.toWord().lsh(INSTRUCTION_WIDTH).toOffset());
191                  break;
192                }
193              }
194            }
195          }
196          ip = Magic.getReturnAddress(fp);
197          fp = Magic.getCallerFramePointer(fp);
198        }
199        if (m != null) {
200          int allocid = (((compiledMethodID & 0x0000ffff) << 15) ^
201                         ((compiledMethodID & 0xffff0000) >> 16) ^
202                         ipOffset.toInt()) & ~0x80000000;
203    
204          /* Now print the location string. */
205          VM.sysWrite('\n');
206          VM.writeHex(allocid);
207          VM.sysWrite('-');
208          VM.sysWrite('>');
209          VM.sysWrite('[');
210          VM.writeHex(compiledMethodID);
211          VM.sysWrite(']');
212          m.getDeclaringClass().getDescriptor().sysWrite();
213          VM.sysWrite(':');
214          m.getName().sysWrite();
215          m.getDescriptor().sysWrite();
216          VM.sysWrite(':');
217          VM.writeHex(bci);
218          VM.sysWrite('\t');
219          RVMType type = tib.getType();
220          type.getDescriptor().sysWrite();
221          VM.sysWrite('\n');
222        }
223        return fp;
224      }
225    
226      /***********************************************************************
227       *
228       * Wrapper methods
229       */
230    
231      @Inline
232      public void updateDeathTime(ObjectReference obj) {
233        MiscHeader.updateDeathTime(obj.toObject());
234      }
235    
236      @Inline
237      public void setDeathTime(ObjectReference ref, Word time_) {
238        MiscHeader.setDeathTime(ref.toObject(), time_);
239      }
240    
241      @Inline
242      public void setLink(ObjectReference ref, ObjectReference link) {
243        MiscHeader.setLink(ref.toObject(), link);
244      }
245    
246      @Inline
247      public void updateTime(Word time_) {
248        MiscHeader.updateTime(time_);
249      }
250    
251      @Inline
252      public Word getOID(ObjectReference ref) {
253        return MiscHeader.getOID(ref.toObject());
254      }
255    
256      @Inline
257      public Word getDeathTime(ObjectReference ref) {
258        return MiscHeader.getDeathTime(ref.toObject());
259      }
260    
261      @Inline
262      public ObjectReference getLink(ObjectReference ref) {
263        return MiscHeader.getLink(ref.toObject());
264      }
265    
266      @Inline
267      public Address getBootImageLink() {
268        return MiscHeader.getBootImageLink();
269      }
270    
271      @Inline
272      public Word getOID() {
273        return MiscHeader.getOID();
274      }
275    
276      @Inline
277      public void setOID(Word oid) {
278        MiscHeader.setOID(oid);
279      }
280    
281      @Inline
282      public int getHeaderSize() {
283        return MiscHeader.getHeaderSize();
284      }
285    
286      @Inline
287      public int getHeaderEndOffset() {
288        return ObjectModel.getHeaderEndOffset();
289      }
290    }