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 }