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.ClassInfo;
044: import org.netbeans.lib.profiler.global.CommonConstants;
045:
046: /**
047: * Specialized subclass of Injector, that provides injection of our standard "recursive" instrumentation -
048: * methodEntry(char methodId) (rootEntry(char methodId)) and methodExit(char methodId) calls - in appropriate places
049: * in TA methods.
050: *
051: * @author Tomas Hurka
052: * @author Misha Dmitriev
053: */
054: class MethodEntryExitCallsInjector extends Injector implements
055: CommonConstants {
056: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
057:
058: // Stuff used for rootEntry(char), methodEntry(char), and methodExit(char) injection interchangeably
059: protected static byte[] injCode1;
060: protected static int injCodeLen1;
061: protected static int injCodeMethodIdxPos1;
062: protected static int injCodeMethodIdPos1;
063:
064: // Stuff used for the equivalent of try { .. } catch (Throwable ex) { methodExit(methodId); throw ex; } around the whole method
065: protected static byte[] injCode2;
066: protected static int injCodeLen2;
067: protected static int injCodeMethodIdxPos2;
068: protected static int injCodeMethodIdPos2;
069:
070: static {
071: initializeInjectedCode();
072: }
073:
074: //~ Instance fields ----------------------------------------------------------------------------------------------------------
075:
076: protected int baseRootCPoolCount; // cpool base for root method type injection cpool fragment
077: protected int injType; // INJ_RECURSIVE_NORMAL_METHOD, INJ_RECURSIVE_ROOT_METHOD, or same with _SAMPLED_ added
078: protected int methodId; // methodId (char parameter value) that methodEntry(methodId) etc. should be invoked with
079:
080: //~ Constructors -------------------------------------------------------------------------------------------------------------
081:
082: MethodEntryExitCallsInjector(ClassInfo clazz,
083: int normalInstrBaseCPoolCount, int rootInstrBaseCPoolCount,
084: int methodIdx, int injType, int methodId) {
085: super (clazz, methodIdx);
086: this .injType = injType;
087: this .methodId = methodId;
088: baseCPoolCount = normalInstrBaseCPoolCount;
089: baseRootCPoolCount = rootInstrBaseCPoolCount;
090: }
091:
092: //~ Methods ------------------------------------------------------------------------------------------------------------------
093:
094: public byte[] instrumentMethod() {
095: // Determine the number of returns in this method
096: int totalReturns = 0;
097: int bci = 0;
098:
099: while (bci < bytecodesLength) {
100: int bc = bytecodes[bci] & 0xFF;
101:
102: if ((bc >= opc_ireturn) && (bc <= opc_return)) {
103: totalReturns++;
104: }
105:
106: bci += opcodeLength(bci);
107: }
108:
109: injectMethodEntry();
110: injectMethodExits(totalReturns);
111: injectGlobalCatch();
112:
113: // Done very conservatively.
114: maxStack += 1;
115:
116: return createPackedMethodInfo();
117: }
118:
119: private static void initializeInjectedCode() {
120: // Injection for methodEntry(char methodId) (rootMethodEntry(char methodId)) and methodExit(char methodId)
121: // the length of the injected code if extended to 8, to avoid worrying about switch statement 4-byte alignment
122: injCodeLen1 = 8;
123: injCode1 = new byte[injCodeLen1];
124: injCode1[0] = (byte) opc_sipush;
125: // Positions 1, 2 are occupied by methodId
126: injCodeMethodIdPos1 = 1;
127: injCode1[3] = (byte) opc_invokestatic;
128: // Positions 4, 5 are occupied by method index
129: injCodeMethodIdxPos1 = 4;
130: injCode1[6] = injCode1[7] = (byte) opc_nop;
131:
132: // Injection for the whole-method all-exceptions try - catch
133: // We do not need to worry about 4-byte alignment since this always goes to the end of a method
134: injCodeLen2 = 9;
135: injCode2 = new byte[injCodeLen2];
136: injCode2[0] = (byte) opc_astore_1;
137: injCode2[1] = (byte) opc_sipush;
138: // Positions 2, 3 are occupied by methodId
139: injCodeMethodIdPos2 = 2;
140: injCode2[4] = (byte) opc_invokestatic;
141: // Positions 5, 6 are occpupied by method index for methodExit()
142: injCodeMethodIdxPos2 = 5;
143: injCode2[7] = (byte) opc_aload_1;
144: injCode2[8] = (byte) opc_athrow;
145: }
146:
147: /**
148: * Injects code that is effectively try { } catch (Throwable ex) { methodExit (); rethrow ex; }
149: * To have methodExit called even in case Errors/Exceptions are thrown.
150: */
151: private void injectGlobalCatch() {
152: int targetMethodIdx;
153:
154: if ((injType == INJ_RECURSIVE_MARKER_METHOD)
155: || (injType == INJ_RECURSIVE_SAMPLED_MARKER_METHOD)) {
156: targetMethodIdx = CPExtensionsRepository.rootContents_MarkerExitMethodIdx
157: + baseRootCPoolCount;
158: } else {
159: targetMethodIdx = CPExtensionsRepository.normalContents_MethodExitMethodIdx
160: + baseCPoolCount;
161: }
162:
163: putU2(injCode2, injCodeMethodIdPos2, methodId);
164: putU2(injCode2, injCodeMethodIdxPos2, targetMethodIdx);
165:
166: int origLen = bytecodesLength;
167: int bci = 0;
168:
169: //int lastInstrBCI = 0;
170: //while (bci < bytecodesLength) {
171: // lastInstrBCI = bci;
172: // bci += opcodeLength(bci);
173: //}
174: // Above is the "theoretically correct" variant of the code: we should determine the starting offset of the
175: // last bytecode of the method, and use it as the end index in the exception table entry that we add. However,
176: // for some reason, it appears that at least in case when in the given method the last bytecode is athrow,
177: // the above code results in the exception thrown by this athrow and not caught by our global handler. This
178: // looks like a bug in the JVM, but anyway, the workaround (?), when we set the last bytecode offset equal
179: // to the start of our handler itself, works. Not sure, though, that it will pass verification... but when
180: // we have per-method verification working, we will be in a much better position to discuss this possible VM bug :-)
181: int lastInstrBCI = bytecodesLength;
182:
183: appendCode(injCode2, injCodeLen2);
184: addExceptionTableEntry(0, lastInstrBCI, origLen, 0);
185:
186: if (maxLocals < 2) {
187: maxLocals = 2;
188: }
189: }
190:
191: private void injectMethodEntry() {
192: int targetMethodIdx = 0;
193:
194: // Prepare the methodEntry(char methodId) or rootEntry(char methodId) code packet that is to be injected
195: if ((injType == INJ_RECURSIVE_ROOT_METHOD)
196: || (injType == INJ_RECURSIVE_SAMPLED_ROOT_METHOD)) {
197: targetMethodIdx = CPExtensionsRepository.rootContents_RootEntryMethodIdx
198: + baseRootCPoolCount;
199: } else if ((injType == INJ_RECURSIVE_MARKER_METHOD)
200: || (injType == INJ_RECURSIVE_SAMPLED_MARKER_METHOD)) {
201: targetMethodIdx = CPExtensionsRepository.rootContents_MarkerEntryMethodIdx
202: + baseRootCPoolCount;
203: } else {
204: targetMethodIdx = CPExtensionsRepository.normalContents_MethodEntryMethodIdx
205: + baseCPoolCount;
206: }
207:
208: putU2(injCode1, injCodeMethodIdxPos1, targetMethodIdx);
209: putU2(injCode1, injCodeMethodIdPos1, methodId);
210:
211: injectCodeAndRewrite(injCode1, injCodeLen1, 0, true);
212: }
213:
214: private void injectMethodExits(int totalReturns) {
215: // Prepare the methodExit(char methodId) code packet
216: int targetMethodIdx;
217:
218: if ((injType == INJ_RECURSIVE_MARKER_METHOD)
219: || (injType == INJ_RECURSIVE_SAMPLED_MARKER_METHOD)) {
220: targetMethodIdx = CPExtensionsRepository.rootContents_MarkerExitMethodIdx
221: + baseRootCPoolCount;
222: } else {
223: targetMethodIdx = CPExtensionsRepository.normalContents_MethodExitMethodIdx
224: + baseCPoolCount;
225: }
226:
227: putU2(injCode1, injCodeMethodIdxPos1, targetMethodIdx);
228:
229: for (int i = 0; i < totalReturns; i++) {
230: int retIdx = -1;
231: int bci = 0;
232:
233: while (bci < bytecodesLength) {
234: int bc = bytecodes[bci] & 0xFF;
235:
236: if ((bc >= opc_ireturn) && (bc <= opc_return)) {
237: retIdx++;
238:
239: if (retIdx == i) {
240: injectCodeAndRewrite(injCode1, injCodeLen1,
241: bci, true);
242:
243: break;
244: }
245: }
246:
247: bci += opcodeLength(bci);
248: }
249: }
250: }
251: }
|