001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.instrumentation;
042:
043: import org.netbeans.lib.profiler.classfile.BaseClassInfo;
044: import org.netbeans.lib.profiler.classfile.ClassInfo;
045: import org.netbeans.lib.profiler.classfile.ClassRepository;
046: import org.netbeans.lib.profiler.classfile.DynamicClassInfo;
047: import org.netbeans.lib.profiler.global.CommonConstants;
048: import java.io.IOException;
049:
050: /**
051: * Specialized subclass of Injector, that provides injection of our object allocation and liveness instrumentation -
052: * ProfilerRuntimeObjAlloc/ProfilerRuntimeObjLiveness.traceObjAlloc(Object obj, char classId) call
053: * after each "new", "anewarray" or "newarray" bytecode.
054: *
055: * @author Tomas Hurka
056: * @author Misha Dmitriev
057: */
058: class ObjLivenessInstrCallsInjector extends Injector implements
059: CommonConstants {
060: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
061:
062: // Stuff used for traceObjAlloc(Object, char)
063: protected static byte[] injectedCode;
064: protected static int injectedCodeLen;
065: protected static int injectedCodeMethodIdxPos;
066: protected static int injectedCodeClassIdPos;
067:
068: static {
069: initializeInjectedCode();
070: }
071:
072: //~ Instance fields ----------------------------------------------------------------------------------------------------------
073:
074: protected boolean[] allUnprofiledClassStatusArray;
075:
076: //~ Constructors -------------------------------------------------------------------------------------------------------------
077:
078: public ObjLivenessInstrCallsInjector(ClassInfo clazz,
079: int baseCPoolCount, int methodIdx,
080: boolean[] allUnprofiledClassStatusArray) {
081: super (clazz, methodIdx);
082: this .baseCPoolCount = baseCPoolCount;
083: this .allUnprofiledClassStatusArray = allUnprofiledClassStatusArray;
084: }
085:
086: //~ Methods ------------------------------------------------------------------------------------------------------------------
087:
088: public byte[] instrumentMethod() {
089: int loaderId = clazz.getLoaderId();
090: int bc;
091: int bci = 0;
092: int nInjections = 0;
093:
094: // Instrument all opc_new, opc_anewarray and opc_newarray instructions, for which allUnprofiledClassStatusArray[classId] != true
095: int opcNewCount = 0;
096:
097: // Instrument all opc_new, opc_anewarray and opc_newarray instructions, for which allUnprofiledClassStatusArray[classId] != true
098: int opcNewToInstr = 0;
099:
100: do {
101: opcNewToInstr = opcNewCount + 1;
102: bci = 0;
103:
104: while (bci < bytecodesLength) {
105: bc = (bytecodes[bci] & 0xFF);
106:
107: if ((bc == opc_new) || (bc == opc_anewarray)
108: || (bc == opc_newarray)
109: || (bc == opc_multianewarray)) {
110: opcNewToInstr--;
111:
112: if (opcNewToInstr == 0) {
113: opcNewCount++;
114:
115: BaseClassInfo refClazz;
116:
117: if ((bc == opc_new) || (bc == opc_anewarray)
118: || (bc == opc_multianewarray)) {
119: int classCPIdx = getU2(bci + 1);
120: String refClassName = clazz
121: .getRefClassName(classCPIdx);
122:
123: if (bc == opc_new) {
124: refClazz = ClassManager
125: .javaClassOrPlaceholderForName(
126: refClassName, loaderId);
127: } else if (bc == opc_anewarray) {
128: refClazz = ClassManager
129: .javaClassForObjectArrayType(refClassName);
130: } else {
131: refClazz = ClassRepository
132: .lookupSpecialClass(refClassName);
133: }
134:
135: if (refClazz == null) {
136: continue; // Warning already issued
137: }
138:
139: int classId = refClazz.getInstrClassId();
140:
141: if ((allUnprofiledClassStatusArray != null)
142: && (allUnprofiledClassStatusArray.length > classId)
143: && allUnprofiledClassStatusArray[classId]) {
144: continue;
145: }
146:
147: if ((bc == opc_anewarray)
148: || (bc == opc_multianewarray)) { // Simply inject the call after the bytecode instruction
149: injectTraceObjAlloc(classId, bci
150: + opcodeLength(bci));
151: nInjections++;
152: } else { // opc_new - we can only inject the call after the corresponding constructor call
153: bci += opcodeLength(bci);
154: bc = (bytecodes[bci] & 0xFF);
155:
156: if ((bc != opc_dup)
157: && (bc != opc_dup_x1)
158: && (bc != opc_dup_x2)) {
159: // see issue http://www.netbeans.org/issues/show_bug.cgi?id=59085
160: // the JBoss JSP compiler generates bytecode that uses opc_dup_x1 in some cases,
161: // something javac would not generate
162: // in this case, injecting extra dup would corrupt the stack, so we cannot perform it
163: // same is expected for opc_dup_x2
164:
165: // No standard 'dup' after 'new'. Can happen if there is a line like 'new Foo()', with no assignment of reference to the new object.
166: // This seems to be a rare case - javac apparently always adds 'dup' to 'new' (it would add 'pop' after it if the object is not used).
167: // We assume that if there is no 'dup' directly after 'new', there is also no 'dup' for this same object later.
168:
169: //System.err.println("*** Gonna inject dup at bci = " + bci + " in method = " + clazz.getName() + "." + clazz.getMethodName(methodIdx) + " , idx = " + methodIdx);
170: injectDup(bci);
171:
172: //System.out.println("*** For " + clazz.getName() + "." + clazz.getMethodName(methodIdx) + " gonna locateConstructor from bci = " + bci);
173: bci = locateConstructorCallForNewOp(
174: bci, bytecodesLength,
175: refClassName);
176:
177: // [fixme]
178: //
179: // unfortunately deinjecting the dump here is not straightforward if bci = -1
180: // as an indication of failure to figure out the correct constructor call
181: // So far this is not happening, the issue that happens with Hibernate goes through the other branch
182: // without injecting dup
183: // see http://www.netbeans.org/issues/show_bug.cgi?id=67346
184: injectTraceObjAllocNoDup(classId,
185: bci);
186: nInjections++;
187: } else {
188: bci = locateConstructorCallForNewOp(
189: bci, bytecodesLength,
190: refClassName);
191:
192: if (bci != -1) {
193: injectTraceObjAlloc(classId,
194: bci);
195: nInjections++;
196: }
197: }
198: }
199: } else { // opc_newarray - primitive array allocation
200:
201: int arrayClassId = getByte(bci + 1);
202: refClazz = ClassManager
203: .javaClassForPrimitiveArrayType(arrayClassId);
204:
205: int classId = refClazz.getInstrClassId();
206:
207: if ((allUnprofiledClassStatusArray == null)
208: || !allUnprofiledClassStatusArray[classId]) {
209: injectTraceObjAlloc(classId, bci + 2);
210: nInjections++;
211: }
212: }
213:
214: break;
215: }
216: }
217:
218: bci += opcodeLength(bci);
219: }
220: } while (opcNewToInstr == 0);
221:
222: if (nInjections == 0) {
223: ((DynamicClassInfo) clazz)
224: .unsetMethodInstrumented(methodIdx);
225: } else {
226: // Done very conservatively.
227: maxStack += 2;
228: }
229:
230: return createPackedMethodInfo();
231: }
232:
233: private static void initializeInjectedCode() {
234: // Code packet for traceObjAlloc(Object obj, char classId)
235: injectedCodeLen = 8;
236: injectedCode = new byte[injectedCodeLen];
237: injectedCode[0] = (byte) opc_dup; // push newly created object to top of stack to pass as the first argument to traceObjAlloc method
238: injectedCode[1] = (byte) opc_sipush; // push char - the actual value will be next two bytes, a second parameter passed to traceObjAlloc method
239: // Positions 2, 3 are occupied by classId
240:
241: injectedCodeClassIdPos = 2;
242: injectedCode[4] = (byte) opc_invokestatic;
243: // Positions 5, 6 are occupied by method index
244: injectedCodeMethodIdxPos = 5;
245: injectedCode[7] = (byte) opc_nop;
246: }
247:
248: private void injectDup(int bci) {
249: byte[] injCode = new byte[] { (byte) opc_dup, (byte) opc_nop,
250: (byte) opc_nop, (byte) opc_nop };
251: injectCodeAndRewrite(injCode, 4, bci, false);
252: }
253:
254: private void injectTraceObjAlloc(int classId, int bci) {
255: // Prepare the traceObjAlloc(Object obj, char classId) code packet that is to be injected
256: int targetMethodIdx = CPExtensionsRepository.memoryProfContents_TraceObjAllocMethodIdx
257: + baseCPoolCount;
258: putU2(injectedCode, injectedCodeMethodIdxPos, targetMethodIdx);
259: putU2(injectedCode, injectedCodeClassIdPos, classId);
260:
261: injectCodeAndRewrite(injectedCode, injectedCodeLen, bci, false);
262: }
263:
264: private void injectTraceObjAllocNoDup(int classId, int bci) {
265: injectedCode[0] = (byte) opc_nop; // Remove the dup
266: // Prepare the traceObjAlloc(Object obj, char classId) code packet that is to be injected
267:
268: int targetMethodIdx = CPExtensionsRepository.memoryProfContents_TraceObjAllocMethodIdx
269: + baseCPoolCount;
270: putU2(injectedCode, injectedCodeMethodIdxPos, targetMethodIdx);
271: putU2(injectedCode, injectedCodeClassIdPos, classId);
272:
273: injectCodeAndRewrite(injectedCode, injectedCodeLen, bci, false);
274: injectedCode[0] = (byte) opc_dup; // Restore dup
275: }
276:
277: private int locateConstructorCallForNewOp(int startBCI,
278: int bytecodesLength, String newOpClassName) {
279: int bc;
280: int bci = startBCI;
281: int nestedNewOps = 0;
282:
283: while (bci < bytecodesLength) {
284: bc = (bytecodes[bci] & 0xFF);
285:
286: if (bc == opc_new) {
287: nestedNewOps++;
288: } else if (bc == opc_invokespecial) {
289: int index = getU2(bci + 1);
290: String[] cms = clazz
291: .getRefMethodsClassNameAndSig(index);
292:
293: if (cms == null) {
294: System.err
295: .println("Failed to locate constant pool ref in: "
296: + clazz.getName()); // NOI18N
297: System.err.println("new Op class: "
298: + newOpClassName); // NOI18N
299: System.err.println("bci: " + bci + ", startBCI: "
300: + startBCI); // NOI18N
301: System.err.println("constant pool ref index: "
302: + index); // NOI18N
303:
304: try {
305: ClassRewriter.saveToDisk(clazz.getName(),
306: ((DynamicClassInfo) clazz)
307: .getClassFileBytes());
308: } catch (IOException e) {
309: System.err
310: .println("Caught exception while dumping class: "
311: + clazz.getName()
312: + ", "
313: + e.getMessage()); // NOI18N
314: e.printStackTrace(System.err);
315: }
316:
317: //debug = true;
318: return -1;
319: }
320:
321: String refClassName = cms[0];
322: String refMethodName = cms[1];
323:
324: if (refMethodName == "<init>") { // NOI18N // It's really a constructor call, not e.g. a call to a private method of 'this'
325:
326: if (refClassName.equals(newOpClassName)
327: && (nestedNewOps == 0)) {
328: bci += opcodeLength(bci);
329:
330: return bci;
331: } else {
332: nestedNewOps--;
333: }
334: }
335: }
336:
337: bci += opcodeLength(bci);
338: }
339:
340: System.err
341: .println("Profiler Warning: Failed to instrument creation of class "
342: + newOpClassName // NOI18N
343: + " in method "
344: + clazz.getName()
345: + "."
346: + clazz.getMethodName(methodIdx)); // NOI18N
347:
348: return -1; // not instrumentable, there is no call to constructor
349: }
350: }
|