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.ProfilerEngineSettings;
044: import org.netbeans.lib.profiler.classfile.BaseClassInfo;
045: import org.netbeans.lib.profiler.classfile.ClassInfo;
046: import org.netbeans.lib.profiler.classfile.ClassRepository;
047: import org.netbeans.lib.profiler.classfile.DynamicClassInfo;
048: import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
049: import java.util.Enumeration;
050:
051: /**
052: * High-level access to functionality that instruments TA methods to generate object allocation and liveness data.
053: * Also has support for removing instrumentation for a subset of classes that the user deemed
054: * "not interesting".
055: *
056: * @author Misha Dmitriev
057: */
058: public class ObjLivenessMethodInstrumentor extends
059: MemoryProfMethodInstrumentor {
060: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
061:
062: static class MethodScanerForBannedInstantiations extends
063: SingleMethodScaner {
064: //~ Instance fields ------------------------------------------------------------------------------------------------------
065:
066: protected boolean[] unprofiledClassStatusArray;
067:
068: //~ Methods --------------------------------------------------------------------------------------------------------------
069:
070: void setUnprofiledClassStatusArray(boolean[] v) {
071: unprofiledClassStatusArray = v;
072: }
073:
074: boolean hasNewOpcodes() {
075: int loaderId = clazz.getLoaderId();
076: int bc;
077: int bci = 0;
078:
079: while (bci < bytecodesLength) {
080: bc = (bytecodes[bci] & 0xFF);
081:
082: if ((bc == opc_new) || (bc == opc_anewarray)
083: || (bc == opc_multianewarray)) {
084: int classCPIdx = getU2(bci + 1);
085: String refClassName = clazz
086: .getRefClassName(classCPIdx);
087: BaseClassInfo refClazz;
088:
089: if (bc == opc_new) {
090: refClazz = ClassManager
091: .javaClassOrPlaceholderForName(
092: refClassName, loaderId);
093: } else if (bc == opc_anewarray) {
094: refClazz = ClassManager
095: .javaClassForObjectArrayType(refClassName);
096: } else {
097: refClazz = ClassRepository
098: .lookupSpecialClass(refClassName);
099: }
100:
101: int classId = refClazz.getInstrClassId();
102:
103: if (classId != -1) {
104: if ((unprofiledClassStatusArray.length > classId)
105: && unprofiledClassStatusArray[classId]) {
106: return true;
107: }
108: }
109: } else if (bc == opc_newarray) {
110: int arrayClassId = getByte(bci + 1);
111: BaseClassInfo refClazz = javaClassForPrimitiveArrayType(arrayClassId);
112: int classId = refClazz.getInstrClassId();
113:
114: if (classId != -1) {
115: if (unprofiledClassStatusArray[classId]) {
116: return true;
117: }
118: }
119: }
120:
121: bci += opcodeLength(bci);
122: }
123:
124: return false;
125: }
126: }
127:
128: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
129:
130: // Values that operationCode can have
131: protected static final int STANDARD_INSTRUMENTATION = 1;
132: protected static final int SELECTIVE_INSTR_REMOVAL = 2;
133:
134: //~ Instance fields ----------------------------------------------------------------------------------------------------------
135:
136: protected MethodScanerForBannedInstantiations msbi;
137: protected boolean[] allUnprofiledClassStatusArray;
138: protected int operationCode; // Depending on this value, use different methods to mark/determine if a method needs rewriting
139: private final ProfilerEngineSettings engineSettings;
140:
141: //~ Constructors -------------------------------------------------------------------------------------------------------------
142:
143: public ObjLivenessMethodInstrumentor(ProfilingSessionStatus status,
144: ProfilerEngineSettings engineSettings, boolean isLiveness) {
145: super (status, isLiveness ? INJ_OBJECT_LIVENESS
146: : INJ_OBJECT_ALLOCATIONS);
147: this .engineSettings = engineSettings;
148: msbi = new MethodScanerForBannedInstantiations();
149: operationCode = STANDARD_INSTRUMENTATION;
150: }
151:
152: //~ Methods ------------------------------------------------------------------------------------------------------------------
153:
154: /**
155: * This is called when object allocation profiling is already active, with the argument where each line
156: * corresponds to a class id, and value true at this line means that the class should not be profiled anymore
157: * (i.e. allocation info for its instances shouldn't be generated). Returns the methods from which the
158: * instrumentation for unprofiled classes is removed (but for others it's still in place).
159: */
160: public Object[] getMethodsToInstrumentUponClassUnprofiling(
161: boolean[] unprofiledClassStatusArray) {
162: operationCode = SELECTIVE_INSTR_REMOVAL;
163: initInstrumentationPackData();
164: msbi.setUnprofiledClassStatusArray(unprofiledClassStatusArray);
165: setAllUnprofiledClassStatusArray(unprofiledClassStatusArray);
166:
167: for (Enumeration e = ClassRepository
168: .getClassEnumerationWithAllVersions(); e
169: .hasMoreElements();) {
170: Object ci = e.nextElement();
171:
172: if (!(ci instanceof DynamicClassInfo)) {
173: continue; // It's a BaseClassInfo, created just for e.g. array classes, or a PlaceholderClassInfo
174: }
175:
176: DynamicClassInfo clazz = (DynamicClassInfo) ci;
177:
178: if (!clazz.isLoaded()) {
179: continue;
180: }
181:
182: if (!clazz.hasInstrumentedMethods()) {
183: continue;
184: }
185:
186: String[] methodNames = clazz.getMethodNames();
187: boolean found = false;
188:
189: for (int i = 0; i < methodNames.length; i++) {
190: if (!clazz.isMethodInstrumented(i)) {
191: continue;
192: }
193:
194: msbi.setClassAndMethod(clazz, i);
195:
196: if (msbi.hasNewOpcodes()) {
197: found = true;
198: clazz.setMethodSpecial(i);
199: nInstrMethods++;
200: }
201: }
202:
203: if (found) {
204: nInstrClasses++;
205: instrClasses.add(clazz);
206: }
207: }
208:
209: Object[] res = createInstrumentedMethodPack();
210: operationCode = STANDARD_INSTRUMENTATION;
211:
212: return res;
213: }
214:
215: /**
216: * Every time the user tells the tool to remove profiling for some class(es), the info about only
217: * those classes currently selected for unprofiling, rather than about all classes ever unprofiled,
218: * is recorded and then passed here via currentUnprofiledClassStatusArray[]. This is done to simplify
219: * scanning instrumented methods for 'new' instructions to unprofile, so that instructions that
220: * have already been unprofiled before don't cause false positives. However, when rewriting methods
221: * that may already contain some unprofiled instantiations (to unprofile more instantiations), or
222: * rewriting fresh uninstrumented methods, we need to use cumulative info about all unprofilings.
223: * That info is collected here, in allUnprofiledClassStatusArray.
224: */
225: protected void setAllUnprofiledClassStatusArray(
226: boolean[] currentUnprofiledClassStatusArray) {
227: int len = currentUnprofiledClassStatusArray.length;
228:
229: if ((allUnprofiledClassStatusArray == null)
230: || (allUnprofiledClassStatusArray.length < len)) {
231: boolean[] old = allUnprofiledClassStatusArray;
232: allUnprofiledClassStatusArray = new boolean[len];
233:
234: if (old != null) {
235: System.arraycopy(old, 0, allUnprofiledClassStatusArray,
236: 0, old.length);
237: }
238: }
239:
240: for (int i = 0; i < len; i++) {
241: if (currentUnprofiledClassStatusArray[i]) {
242: allUnprofiledClassStatusArray[i] = true;
243: }
244: }
245: }
246:
247: protected byte[] instrumentMethod(DynamicClassInfo clazz,
248: int methodIdx) {
249: return InstrumentationFactory
250: .instrumentForMemoryProfiling(clazz, methodIdx,
251: allUnprofiledClassStatusArray, injType,
252: getRuntimeProfilingPoints(engineSettings
253: .getRuntimeProfilingPoints(), clazz,
254: methodIdx));
255: }
256:
257: protected boolean methodNeedsInstrumentation(ClassInfo clazz,
258: int methodIdx) {
259: // TODO: hasNewOpcodes must be called in any case, because it has side effects!
260: boolean ni = hasNewOpcodes(clazz, methodIdx, true);
261:
262: return ni
263: || (getRuntimeProfilingPoints(engineSettings
264: .getRuntimeProfilingPoints(), clazz, methodIdx).length > 0);
265: }
266:
267: protected boolean methodNeedsRewriting(DynamicClassInfo clazz,
268: int methodIdx) {
269: if (operationCode == STANDARD_INSTRUMENTATION) {
270: return clazz.isMethodInstrumented(methodIdx);
271: } else { // SELECTIVE_INSTR_REMOVAL
272:
273: boolean res = clazz.isMethodSpecial(methodIdx);
274: clazz.unsetMethodSpecial(methodIdx);
275:
276: return res;
277: }
278: }
279: }
|