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.ProfilingSessionStatus;
048: import java.util.ArrayList;
049:
050: /**
051: * Base class providing common functionality for instrumenting TA methods to generate object allocation/liveness data.
052: * The fact that there are two classes at this time, MemoryProfMethodInstrumentor and ObjLivenessMethodInstrumentor, is
053: * explained by purely historical reasons - in the past we had instrumentation implemented differently for object allocation
054: * and liveness profiling. Now the same kind of instrumentation is used for both profiling types (the exact method names
055: * for injected calls are different, but the way of injecting these calls and their signatures are exactly the same).
056: *
057: * In principle, MemoryProfMethodInstrumentor and ObjLivenessMethodInstrumentor can be merged, but there is no compelling
058: * need for that. Furthermore, if in future say some different memory instrumentation kind is introduced, this division
059: * may help.
060: *
061: * @author Misha Dmitriev
062: */
063: public abstract class MemoryProfMethodInstrumentor extends ClassManager {
064: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
065:
066: static class MethodScanerForNewOpcodes extends SingleMethodScaner {
067: //~ Methods --------------------------------------------------------------------------------------------------------------
068:
069: boolean hasNewArrayOpcodes(MemoryProfMethodInstrumentor minstr,
070: boolean checkForOpcNew) {
071: int loaderId = clazz.getLoaderId();
072: boolean found = false;
073: int bc;
074: int bci = 0;
075:
076: while (bci < bytecodesLength) {
077: bc = (bytecodes[bci] & 0xFF);
078:
079: if ((bc == opc_new) && checkForOpcNew) {
080: found = true;
081:
082: int classCPIdx = getU2(bci + 1);
083: String refClassName = clazz
084: .getRefClassName(classCPIdx);
085: BaseClassInfo refClazz = javaClassOrPlaceholderForName(
086: refClassName, loaderId);
087:
088: if (refClazz.getInstrClassId() == -1) {
089: refClazz.setInstrClassId(minstr
090: .getNextClassId(refClazz.getName()));
091: }
092: } else if ((bc == opc_anewarray)
093: || (bc == opc_multianewarray)) {
094: found = true;
095:
096: int classCPIdx = getU2(bci + 1);
097: String refClassName = clazz
098: .getRefClassName(classCPIdx);
099: BaseClassInfo refClazz = (bc == opc_anewarray) ? javaClassForObjectArrayType(refClassName)
100: : ClassRepository
101: .lookupSpecialClass(refClassName);
102:
103: if (refClazz != null) { // Warning already issued
104:
105: if (refClazz.getInstrClassId() == -1) {
106: refClazz
107: .setInstrClassId(minstr
108: .getNextClassId(refClazz
109: .getName()));
110: }
111: }
112: } else if (bc == opc_newarray) {
113: found = true;
114:
115: int arrayClassId = getByte(bci + 1);
116: BaseClassInfo refClazz = javaClassForPrimitiveArrayType(arrayClassId);
117:
118: if (refClazz.getInstrClassId() == -1) {
119: refClazz.setInstrClassId(minstr
120: .getNextClassId(refClazz.getName()));
121: }
122: }
123:
124: bci += opcodeLength(bci);
125: }
126:
127: return found;
128: }
129: }
130:
131: //~ Instance fields ----------------------------------------------------------------------------------------------------------
132:
133: protected ArrayList instrClasses = new ArrayList();
134: protected MethodScanerForNewOpcodes msfno;
135: protected String[] instantiatableClasses;
136: protected int injType;
137: protected int instrClassId;
138: protected int nInstantiatableClasses;
139: protected int nInstrClasses;
140: protected int nInstrMethods;
141:
142: //~ Constructors -------------------------------------------------------------------------------------------------------------
143:
144: public MemoryProfMethodInstrumentor(ProfilingSessionStatus status,
145: int injType) {
146: super (status);
147: this .status = status;
148: instantiatableClasses = new String[100];
149: this .injType = injType;
150: msfno = new MethodScanerForNewOpcodes();
151: }
152:
153: //~ Methods ------------------------------------------------------------------------------------------------------------------
154:
155: public Object[] getInitialMethodsToInstrument(
156: String[] loadedClasses, int[] loadedClassLoaderIds,
157: byte[][] cachedClassFileBytes) {
158: resetLoadedClassData();
159: initInstrumentationPackData();
160: instrClassId = 0;
161:
162: storeClassFileBytesForCustomLoaderClasses(loadedClasses,
163: loadedClassLoaderIds, cachedClassFileBytes);
164:
165: for (int i = 0; i < loadedClasses.length; i++) {
166: if (loadedClasses[i].equals("java.lang.Object")) {
167: continue; // NOI18N
168: }
169:
170: findAndMarkMethodsToInstrumentInClass(loadedClasses[i],
171: loadedClassLoaderIds[i]);
172: }
173:
174: return createInstrumentedMethodPack();
175: }
176:
177: public String[] getInstantiatableClasses() {
178: return instantiatableClasses;
179: }
180:
181: public Object[] getMethodsToInstrumentUponClassLoad(
182: String className, int classLoaderId) {
183: initInstrumentationPackData();
184: findAndMarkMethodsToInstrumentInClass(className, classLoaderId);
185:
186: return createInstrumentedMethodPack();
187: }
188:
189: public int getNInstantiatableClasses() {
190: return nInstantiatableClasses;
191: }
192:
193: /** Checks if there are any methods in this class that need to be instrumented. */
194: protected void findAndMarkMethodsToInstrumentInClass(
195: String className, int classLoaderId) {
196: DynamicClassInfo clazz = javaClassForName(className,
197: classLoaderId);
198:
199: if (clazz == null) {
200: return; // Warning has already been reported
201: }
202:
203: if (!clazz.isLoaded()) {
204: clazz.setLoaded(true);
205:
206: // We assign an ID to class no matter whether or not this class is going to be instantiated anywhere in the program
207: // As a result, we may have quite some classes that are in the appropriate table in 'status', but have zero objects
208: // associated with them forever.
209: // I am not sure why this is done here, but perhaps there was a good reason for this. Need to comment such things immediately...
210: if ((clazz.getInstrClassId() == -1) && !clazz.isInterface()) {
211: clazz.setInstrClassId(getNextClassId(clazz.getName()));
212: }
213:
214: String[] methodNames = clazz.getMethodNames();
215: boolean found = false;
216:
217: for (int i = 0; i < methodNames.length; i++) {
218: if (clazz.isMethodNative(i)
219: || clazz.isMethodAbstract(i)) {
220: clazz.setMethodUnscannable(i);
221:
222: continue;
223: }
224:
225: if (methodNeedsInstrumentation(clazz, i)) {
226: nInstrMethods++;
227: clazz.setMethodInstrumented(i);
228: found = true;
229: }
230: }
231:
232: if (found) {
233: nInstrClasses++;
234: instrClasses.add(clazz);
235: }
236: }
237: }
238:
239: protected void initInstrumentationPackData() {
240: instrClasses.clear();
241: nInstrClasses = nInstrMethods = 0;
242: nInstantiatableClasses = 0;
243: }
244:
245: protected abstract boolean methodNeedsInstrumentation(
246: ClassInfo clazz, int methodIdx);
247:
248: /** Creates a multi-class packet of instrumented methods or classes */
249: protected Object[] createInstrumentedMethodPack() {
250: if (nInstrMethods == 0) {
251: return null;
252: }
253:
254: return createInstrumentedMethodPack15();
255: }
256:
257: protected boolean hasNewOpcodes(ClassInfo clazz, int methodIdx,
258: boolean checkForOpcNew) {
259: msfno.setClassAndMethod(clazz, methodIdx);
260:
261: return msfno.hasNewArrayOpcodes(this , checkForOpcNew);
262: }
263:
264: protected abstract byte[] instrumentMethod(DynamicClassInfo clazz,
265: int methodIdx);
266:
267: protected boolean methodNeedsRewriting(DynamicClassInfo clazz,
268: int methodIdx) {
269: return clazz.isMethodInstrumented(methodIdx);
270: }
271:
272: int getNextClassId(String className) {
273: if (nInstantiatableClasses == instantiatableClasses.length) {
274: String[] oldInstantiatableClasses = instantiatableClasses;
275: instantiatableClasses = new String[oldInstantiatableClasses.length + 100];
276: System.arraycopy(oldInstantiatableClasses, 0,
277: instantiatableClasses, 0,
278: oldInstantiatableClasses.length);
279: }
280:
281: instantiatableClasses[nInstantiatableClasses++] = className;
282: status.updateAllocatedInstancesCountInfoInClient(className);
283:
284: return instrClassId++;
285: }
286:
287: /** Creates the 1.5-style array of instrumented class files. */
288: private Object[] createInstrumentedMethodPack15() {
289: String[] instrMethodClasses = new String[nInstrClasses];
290: int[] instrClassLoaderIds = new int[nInstrClasses];
291: byte[][] replacementClassFileBytes = new byte[nInstrClasses][];
292:
293: for (int j = 0; j < nInstrClasses; j++) {
294: DynamicClassInfo clazz = (DynamicClassInfo) instrClasses
295: .get(j);
296: instrMethodClasses[j] = clazz.getName().replace('/', '.'); // NOI18N
297: instrClassLoaderIds[j] = clazz.getLoaderId();
298:
299: String[] methodNames = clazz.getMethodNames();
300: int nMethods = methodNames.length;
301: byte[][] replacementMethodInfos = new byte[nMethods][];
302:
303: DynamicConstantPoolExtension.getCPFragment(clazz, injType);
304:
305: for (int i = 0; i < nMethods; i++) {
306: if (methodNeedsRewriting(clazz, i)) {
307: replacementMethodInfos[i] = instrumentMethod(clazz,
308: i);
309: } else {
310: replacementMethodInfos[i] = clazz.getMethodInfo(i);
311: }
312: }
313:
314: DynamicConstantPoolExtension wholeECP = DynamicConstantPoolExtension
315: .getAllAddedCPFragments(clazz);
316: int nAddedCPEntries = wholeECP.getNEntries();
317: byte[] addedCPContents = wholeECP.getContents();
318: replacementClassFileBytes[j] = ClassRewriter
319: .rewriteClassFile(clazz, replacementMethodInfos,
320: nAddedCPEntries, addedCPContents);
321: }
322:
323: return new Object[] { instrMethodClasses, instrClassLoaderIds,
324: replacementClassFileBytes };
325: }
326: }
|